...

Source file src/golang.org/x/tools/internal/gcimporter/bexport_test.go

Documentation: golang.org/x/tools/internal/gcimporter

     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  
     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  		// TODO(gri): enable separate emission of Embedded interfaces
    43  		// and ExplicitMethods then use this logic.
    44  		// if x.NumEmbeddeds() != y.NumEmbeddeds() {
    45  		// 	return fmt.Errorf("unequal number of embedded interfaces: %d vs %d",
    46  		// 		x.NumEmbeddeds(), y.NumEmbeddeds())
    47  		// }
    48  		// for i := 0; i < x.NumEmbeddeds(); i++ {
    49  		// 	xi := x.Embedded(i)
    50  		// 	yi := y.Embedded(i)
    51  		// 	if xi.String() != yi.String() {
    52  		// 		return fmt.Errorf("mismatched %th embedded interface: %s vs %s",
    53  		// 			i, xi, yi)
    54  		// 	}
    55  		// }
    56  		// if x.NumExplicitMethods() != y.NumExplicitMethods() {
    57  		// 	return fmt.Errorf("unequal methods: %d vs %d",
    58  		// 		x.NumExplicitMethods(), y.NumExplicitMethods())
    59  		// }
    60  		// for i := 0; i < x.NumExplicitMethods(); i++ {
    61  		// 	xm := x.ExplicitMethod(i)
    62  		// 	ym := y.ExplicitMethod(i)
    63  		// 	if xm.Name() != ym.Name() {
    64  		// 		return fmt.Errorf("mismatched %th method: %s vs %s", i, xm, ym)
    65  		// 	}
    66  		// 	if err := equalType(xm.Type(), ym.Type()); err != nil {
    67  		// 		return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
    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  		// Constraints are handled explicitly in the *TypeParam case below, so we
    85  		// don't yet need to consider embeddeds here.
    86  		// TODO(rfindley): consider the type set here.
    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  			// TODO(adonovan): fix: this assertion fires for interface methods.
   141  			// The type of the receiver of an interface method is a named type
   142  			// if the Package was loaded from export data, or an unnamed (interface)
   143  			// type if the Package was produced by type-checking ASTs.
   144  			// if err := equalType(x.Recv().Type(), y.Recv().Type()); err != nil {
   145  			// 	return fmt.Errorf("receiver: %s", err)
   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  		// For now, just compare constraints by type string to short-circuit
   195  		// cycles. We have to make interfaces explicit as export data currently
   196  		// doesn't support marking interfaces as implicit.
   197  		// TODO(rfindley): remove makeExplicit once export data contains an
   198  		// implicit bit.
   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  // cmpNamed compares two named types x and y, returning an error for any
   212  // discrepancies. It does not compare their underlying types.
   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  	// Unfortunately method sorting is not canonical, so sort before comparing.
   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  		// Calling equalType here leads to infinite recursion, so just compare
   246  		// strings.
   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  // makeExplicit returns an explicit version of typ, if typ is an implicit
   255  // interface. Otherwise it returns typ unmodified.
   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  // TestVeryLongFile tests the position of an import object declared in
   296  // a very long input file.  Line numbers greater than maxlines are
   297  // reported as line 1, not garbage or token.NoPos.
   298  func TestVeryLongFile(t *testing.T) {
   299  	// parse and typecheck
   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  	// export
   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  	// import
   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  	// compare
   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