1// 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 (
8	"fmt"
9	"go/ast"
10	"go/build"
11	"go/constant"
12	"go/parser"
13	"go/token"
14	"go/types"
15	"reflect"
16	"runtime"
17	"strings"
18	"testing"
19
20	"golang.org/x/tools/go/buildutil"
21	"golang.org/x/tools/go/internal/gcimporter"
22	"golang.org/x/tools/go/loader"
23)
24
25var isRace = false
26
27func TestBExportData_stdlib(t *testing.T) {
28	if runtime.Compiler == "gccgo" {
29		t.Skip("gccgo standard library is inaccessible")
30	}
31	if runtime.GOOS == "android" {
32		t.Skipf("incomplete std lib on %s", runtime.GOOS)
33	}
34	if isRace {
35		t.Skipf("stdlib tests take too long in race mode and flake on builders")
36	}
37
38	// Load, parse and type-check the program.
39	ctxt := build.Default // copy
40	ctxt.GOPATH = ""      // disable GOPATH
41	conf := loader.Config{
42		Build:       &ctxt,
43		AllowErrors: true,
44	}
45	for _, path := range buildutil.AllPackages(conf.Build) {
46		conf.Import(path)
47	}
48
49	// Create a package containing type and value errors to ensure
50	// they are properly encoded/decoded.
51	f, err := conf.ParseFile("haserrors/haserrors.go", `package haserrors
52const UnknownValue = "" + 0
53type UnknownType undefined
54`)
55	if err != nil {
56		t.Fatal(err)
57	}
58	conf.CreateFromFiles("haserrors", f)
59
60	prog, err := conf.Load()
61	if err != nil {
62		t.Fatalf("Load failed: %v", err)
63	}
64
65	numPkgs := len(prog.AllPackages)
66	if want := 248; numPkgs < want {
67		t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
68	}
69
70	for pkg, info := range prog.AllPackages {
71		if info.Files == nil {
72			continue // empty directory
73		}
74		exportdata, err := gcimporter.BExportData(conf.Fset, pkg)
75		if err != nil {
76			t.Fatal(err)
77		}
78
79		imports := make(map[string]*types.Package)
80		fset2 := token.NewFileSet()
81		n, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg.Path())
82		if err != nil {
83			t.Errorf("BImportData(%s): %v", pkg.Path(), err)
84			continue
85		}
86		if n != len(exportdata) {
87			t.Errorf("BImportData(%s) decoded %d bytes, want %d",
88				pkg.Path(), n, len(exportdata))
89		}
90
91		// Compare the packages' corresponding members.
92		for _, name := range pkg.Scope().Names() {
93			if !ast.IsExported(name) {
94				continue
95			}
96			obj1 := pkg.Scope().Lookup(name)
97			obj2 := pkg2.Scope().Lookup(name)
98			if obj2 == nil {
99				t.Errorf("%s.%s not found, want %s", pkg.Path(), name, obj1)
100				continue
101			}
102
103			fl1 := fileLine(conf.Fset, obj1)
104			fl2 := fileLine(fset2, obj2)
105			if fl1 != fl2 {
106				t.Errorf("%s.%s: got posn %s, want %s",
107					pkg.Path(), name, fl2, fl1)
108			}
109
110			if err := equalObj(obj1, obj2); err != nil {
111				t.Errorf("%s.%s: %s\ngot:  %s\nwant: %s",
112					pkg.Path(), name, err, obj2, obj1)
113			}
114		}
115	}
116}
117
118func fileLine(fset *token.FileSet, obj types.Object) string {
119	posn := fset.Position(obj.Pos())
120	return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
121}
122
123// equalObj reports how x and y differ.  They are assumed to belong to
124// different universes so cannot be compared directly.
125func equalObj(x, y types.Object) error {
126	if reflect.TypeOf(x) != reflect.TypeOf(y) {
127		return fmt.Errorf("%T vs %T", x, y)
128	}
129	xt := x.Type()
130	yt := y.Type()
131	switch x.(type) {
132	case *types.Var, *types.Func:
133		// ok
134	case *types.Const:
135		xval := x.(*types.Const).Val()
136		yval := y.(*types.Const).Val()
137		// Use string comparison for floating-point values since rounding is permitted.
138		if constant.Compare(xval, token.NEQ, yval) &&
139			!(xval.Kind() == constant.Float && xval.String() == yval.String()) {
140			return fmt.Errorf("unequal constants %s vs %s", xval, yval)
141		}
142	case *types.TypeName:
143		xt = xt.Underlying()
144		yt = yt.Underlying()
145	default:
146		return fmt.Errorf("unexpected %T", x)
147	}
148	return equalType(xt, yt)
149}
150
151func equalType(x, y types.Type) error {
152	if reflect.TypeOf(x) != reflect.TypeOf(y) {
153		return fmt.Errorf("unequal kinds: %T vs %T", x, y)
154	}
155	switch x := x.(type) {
156	case *types.Interface:
157		y := y.(*types.Interface)
158		// TODO(gri): enable separate emission of Embedded interfaces
159		// and ExplicitMethods then use this logic.
160		// if x.NumEmbeddeds() != y.NumEmbeddeds() {
161		// 	return fmt.Errorf("unequal number of embedded interfaces: %d vs %d",
162		// 		x.NumEmbeddeds(), y.NumEmbeddeds())
163		// }
164		// for i := 0; i < x.NumEmbeddeds(); i++ {
165		// 	xi := x.Embedded(i)
166		// 	yi := y.Embedded(i)
167		// 	if xi.String() != yi.String() {
168		// 		return fmt.Errorf("mismatched %th embedded interface: %s vs %s",
169		// 			i, xi, yi)
170		// 	}
171		// }
172		// if x.NumExplicitMethods() != y.NumExplicitMethods() {
173		// 	return fmt.Errorf("unequal methods: %d vs %d",
174		// 		x.NumExplicitMethods(), y.NumExplicitMethods())
175		// }
176		// for i := 0; i < x.NumExplicitMethods(); i++ {
177		// 	xm := x.ExplicitMethod(i)
178		// 	ym := y.ExplicitMethod(i)
179		// 	if xm.Name() != ym.Name() {
180		// 		return fmt.Errorf("mismatched %th method: %s vs %s", i, xm, ym)
181		// 	}
182		// 	if err := equalType(xm.Type(), ym.Type()); err != nil {
183		// 		return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
184		// 	}
185		// }
186		if x.NumMethods() != y.NumMethods() {
187			return fmt.Errorf("unequal methods: %d vs %d",
188				x.NumMethods(), y.NumMethods())
189		}
190		for i := 0; i < x.NumMethods(); i++ {
191			xm := x.Method(i)
192			ym := y.Method(i)
193			if xm.Name() != ym.Name() {
194				return fmt.Errorf("mismatched %dth method: %s vs %s", i, xm, ym)
195			}
196			if err := equalType(xm.Type(), ym.Type()); err != nil {
197				return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
198			}
199		}
200	case *types.Array:
201		y := y.(*types.Array)
202		if x.Len() != y.Len() {
203			return fmt.Errorf("unequal array lengths: %d vs %d", x.Len(), y.Len())
204		}
205		if err := equalType(x.Elem(), y.Elem()); err != nil {
206			return fmt.Errorf("array elements: %s", err)
207		}
208	case *types.Basic:
209		y := y.(*types.Basic)
210		if x.Kind() != y.Kind() {
211			return fmt.Errorf("unequal basic types: %s vs %s", x, y)
212		}
213	case *types.Chan:
214		y := y.(*types.Chan)
215		if x.Dir() != y.Dir() {
216			return fmt.Errorf("unequal channel directions: %d vs %d", x.Dir(), y.Dir())
217		}
218		if err := equalType(x.Elem(), y.Elem()); err != nil {
219			return fmt.Errorf("channel elements: %s", err)
220		}
221	case *types.Map:
222		y := y.(*types.Map)
223		if err := equalType(x.Key(), y.Key()); err != nil {
224			return fmt.Errorf("map keys: %s", err)
225		}
226		if err := equalType(x.Elem(), y.Elem()); err != nil {
227			return fmt.Errorf("map values: %s", err)
228		}
229	case *types.Named:
230		y := y.(*types.Named)
231		if x.String() != y.String() {
232			return fmt.Errorf("unequal named types: %s vs %s", x, y)
233		}
234	case *types.Pointer:
235		y := y.(*types.Pointer)
236		if err := equalType(x.Elem(), y.Elem()); err != nil {
237			return fmt.Errorf("pointer elements: %s", err)
238		}
239	case *types.Signature:
240		y := y.(*types.Signature)
241		if err := equalType(x.Params(), y.Params()); err != nil {
242			return fmt.Errorf("parameters: %s", err)
243		}
244		if err := equalType(x.Results(), y.Results()); err != nil {
245			return fmt.Errorf("results: %s", err)
246		}
247		if x.Variadic() != y.Variadic() {
248			return fmt.Errorf("unequal variadicity: %t vs %t",
249				x.Variadic(), y.Variadic())
250		}
251		if (x.Recv() != nil) != (y.Recv() != nil) {
252			return fmt.Errorf("unequal receivers: %s vs %s", x.Recv(), y.Recv())
253		}
254		if x.Recv() != nil {
255			// TODO(adonovan): fix: this assertion fires for interface methods.
256			// The type of the receiver of an interface method is a named type
257			// if the Package was loaded from export data, or an unnamed (interface)
258			// type if the Package was produced by type-checking ASTs.
259			// if err := equalType(x.Recv().Type(), y.Recv().Type()); err != nil {
260			// 	return fmt.Errorf("receiver: %s", err)
261			// }
262		}
263	case *types.Slice:
264		y := y.(*types.Slice)
265		if err := equalType(x.Elem(), y.Elem()); err != nil {
266			return fmt.Errorf("slice elements: %s", err)
267		}
268	case *types.Struct:
269		y := y.(*types.Struct)
270		if x.NumFields() != y.NumFields() {
271			return fmt.Errorf("unequal struct fields: %d vs %d",
272				x.NumFields(), y.NumFields())
273		}
274		for i := 0; i < x.NumFields(); i++ {
275			xf := x.Field(i)
276			yf := y.Field(i)
277			if xf.Name() != yf.Name() {
278				return fmt.Errorf("mismatched fields: %s vs %s", xf, yf)
279			}
280			if err := equalType(xf.Type(), yf.Type()); err != nil {
281				return fmt.Errorf("struct field %s: %s", xf.Name(), err)
282			}
283			if x.Tag(i) != y.Tag(i) {
284				return fmt.Errorf("struct field %s has unequal tags: %q vs %q",
285					xf.Name(), x.Tag(i), y.Tag(i))
286			}
287		}
288	case *types.Tuple:
289		y := y.(*types.Tuple)
290		if x.Len() != y.Len() {
291			return fmt.Errorf("unequal tuple lengths: %d vs %d", x.Len(), y.Len())
292		}
293		for i := 0; i < x.Len(); i++ {
294			if err := equalType(x.At(i).Type(), y.At(i).Type()); err != nil {
295				return fmt.Errorf("tuple element %d: %s", i, err)
296			}
297		}
298	}
299	return nil
300}
301
302// TestVeryLongFile tests the position of an import object declared in
303// a very long input file.  Line numbers greater than maxlines are
304// reported as line 1, not garbage or token.NoPos.
305func TestVeryLongFile(t *testing.T) {
306	// parse and typecheck
307	longFile := "package foo" + strings.Repeat("\n", 123456) + "var X int"
308	fset1 := token.NewFileSet()
309	f, err := parser.ParseFile(fset1, "foo.go", longFile, 0)
310	if err != nil {
311		t.Fatal(err)
312	}
313	var conf types.Config
314	pkg, err := conf.Check("foo", fset1, []*ast.File{f}, nil)
315	if err != nil {
316		t.Fatal(err)
317	}
318
319	// export
320	exportdata, err := gcimporter.BExportData(fset1, pkg)
321	if err != nil {
322		t.Fatal(err)
323	}
324
325	// import
326	imports := make(map[string]*types.Package)
327	fset2 := token.NewFileSet()
328	_, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg.Path())
329	if err != nil {
330		t.Fatalf("BImportData(%s): %v", pkg.Path(), err)
331	}
332
333	// compare
334	posn1 := fset1.Position(pkg.Scope().Lookup("X").Pos())
335	posn2 := fset2.Position(pkg2.Scope().Lookup("X").Pos())
336	if want := "foo.go:1:1"; posn2.String() != want {
337		t.Errorf("X position = %s, want %s (orig was %s)",
338			posn2, want, posn1)
339	}
340}
341
342const src = `
343package p
344
345type (
346	T0 = int32
347	T1 = struct{}
348	T2 = struct{ T1 }
349	Invalid = foo // foo is undeclared
350)
351`
352
353func checkPkg(t *testing.T, pkg *types.Package, label string) {
354	T1 := types.NewStruct(nil, nil)
355	T2 := types.NewStruct([]*types.Var{types.NewField(0, pkg, "T1", T1, true)}, nil)
356
357	for _, test := range []struct {
358		name string
359		typ  types.Type
360	}{
361		{"T0", types.Typ[types.Int32]},
362		{"T1", T1},
363		{"T2", T2},
364		{"Invalid", types.Typ[types.Invalid]},
365	} {
366		obj := pkg.Scope().Lookup(test.name)
367		if obj == nil {
368			t.Errorf("%s: %s not found", label, test.name)
369			continue
370		}
371		tname, _ := obj.(*types.TypeName)
372		if tname == nil {
373			t.Errorf("%s: %v not a type name", label, obj)
374			continue
375		}
376		if !tname.IsAlias() {
377			t.Errorf("%s: %v: not marked as alias", label, tname)
378			continue
379		}
380		if got := tname.Type(); !types.Identical(got, test.typ) {
381			t.Errorf("%s: %v: got %v; want %v", label, tname, got, test.typ)
382		}
383	}
384}
385
386func TestTypeAliases(t *testing.T) {
387	// parse and typecheck
388	fset1 := token.NewFileSet()
389	f, err := parser.ParseFile(fset1, "p.go", src, 0)
390	if err != nil {
391		t.Fatal(err)
392	}
393	var conf types.Config
394	pkg1, err := conf.Check("p", fset1, []*ast.File{f}, nil)
395	if err == nil {
396		// foo in undeclared in src; we should see an error
397		t.Fatal("invalid source type-checked without error")
398	}
399	if pkg1 == nil {
400		// despite incorrect src we should see a (partially) type-checked package
401		t.Fatal("nil package returned")
402	}
403	checkPkg(t, pkg1, "export")
404
405	// export
406	exportdata, err := gcimporter.BExportData(fset1, pkg1)
407	if err != nil {
408		t.Fatal(err)
409	}
410
411	// import
412	imports := make(map[string]*types.Package)
413	fset2 := token.NewFileSet()
414	_, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg1.Path())
415	if err != nil {
416		t.Fatalf("BImportData(%s): %v", pkg1.Path(), err)
417	}
418	checkPkg(t, pkg2, "import")
419}
420