golang.org/x/tools@v0.21.0/internal/gcimporter/bexport_test.go (about) 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 }