Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 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 ( |
Alan Donovan | ad74ff6 | 2023-05-11 18:07:57 -0400 | [diff] [blame] | 8 | "bytes" |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 9 | "fmt" |
| 10 | "go/ast" |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 11 | "go/parser" |
| 12 | "go/token" |
| 13 | "go/types" |
Matthew Dempsky | b79f76f | 2021-02-15 23:42:21 -0800 | [diff] [blame] | 14 | "path/filepath" |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 15 | "reflect" |
| 16 | "runtime" |
Robert Findley | 81f084e | 2021-10-18 12:23:03 -0400 | [diff] [blame] | 17 | "sort" |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 18 | "strings" |
| 19 | "testing" |
| 20 | |
Alan Donovan | affa603 | 2022-11-04 11:09:44 -0400 | [diff] [blame] | 21 | "golang.org/x/tools/internal/gcimporter" |
Robert Findley | 91c880c | 2021-09-20 10:42:55 -0400 | [diff] [blame] | 22 | "golang.org/x/tools/internal/typeparams" |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 23 | ) |
| 24 | |
| 25 | var isRace = false |
| 26 | |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 27 | func fileLine(fset *token.FileSet, obj types.Object) string { |
| 28 | posn := fset.Position(obj.Pos()) |
Matthew Dempsky | 67e49ef | 2021-02-17 13:32:28 -0800 | [diff] [blame] | 29 | filename := filepath.Clean(strings.ReplaceAll(posn.Filename, "$GOROOT", runtime.GOROOT())) |
| 30 | return fmt.Sprintf("%s:%d", filename, posn.Line) |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 31 | } |
| 32 | |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 33 | func 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 Findley | ce04ca3 | 2021-10-14 17:46:40 -0400 | [diff] [blame] | 82 | // 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 Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 85 | 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 Findley | 9134808 | 2022-01-06 16:54:44 -0500 | [diff] [blame] | 116 | return cmpNamed(x, y) |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 117 | 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 Findley | 91c880c | 2021-09-20 10:42:55 -0400 | [diff] [blame] | 146 | 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 Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 152 | 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 Findley | 91c880c | 2021-09-20 10:42:55 -0400 | [diff] [blame] | 187 | case *typeparams.TypeParam: |
| 188 | y := y.(*typeparams.TypeParam) |
Robert Findley | 25e1ac4 | 2022-01-20 11:28:07 -0500 | [diff] [blame] | 189 | if x.String() != y.String() { |
Robert Findley | 91c880c | 2021-09-20 10:42:55 -0400 | [diff] [blame] | 190 | 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 Findley | ce04ca3 | 2021-10-14 17:46:40 -0400 | [diff] [blame] | 193 | // 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 Findley | 25e1ac4 | 2022-01-20 11:28:07 -0500 | [diff] [blame] | 197 | xc := makeExplicit(x.Constraint()).String() |
| 198 | yc := makeExplicit(y.Constraint()).String() |
Robert Findley | 91c880c | 2021-09-20 10:42:55 -0400 | [diff] [blame] | 199 | 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 Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 205 | } |
| 206 | return nil |
| 207 | } |
| 208 | |
Robert Findley | 9134808 | 2022-01-06 16:54:44 -0500 | [diff] [blame] | 209 | // cmpNamed compares two named types x and y, returning an error for any |
| 210 | // discrepancies. It does not compare their underlying types. |
| 211 | func cmpNamed(x, y *types.Named) error { |
| 212 | xOrig := typeparams.NamedTypeOrigin(x) |
| 213 | yOrig := typeparams.NamedTypeOrigin(y) |
Robert Findley | 25e1ac4 | 2022-01-20 11:28:07 -0500 | [diff] [blame] | 214 | if xOrig.String() != yOrig.String() { |
Robert Findley | 9134808 | 2022-01-06 16:54:44 -0500 | [diff] [blame] | 215 | 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 Findley | 25e1ac4 | 2022-01-20 11:28:07 -0500 | [diff] [blame] | 245 | if xm.String() != ym.String() { |
Robert Findley | 9134808 | 2022-01-06 16:54:44 -0500 | [diff] [blame] | 246 | return fmt.Errorf("unequal methods: %s vs %s", x, y) |
| 247 | } |
| 248 | } |
| 249 | return nil |
| 250 | } |
| 251 | |
Robert Findley | ce04ca3 | 2021-10-14 17:46:40 -0400 | [diff] [blame] | 252 | // makeExplicit returns an explicit version of typ, if typ is an implicit |
| 253 | // interface. Otherwise it returns typ unmodified. |
| 254 | func 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 Findley | 91c880c | 2021-09-20 10:42:55 -0400 | [diff] [blame] | 269 | func 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 | |
| 281 | func 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 Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 293 | // 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. |
| 296 | func 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 Donovan | ad74ff6 | 2023-05-11 18:07:57 -0400 | [diff] [blame] | 311 | var out bytes.Buffer |
| 312 | if err := gcimporter.IExportData(&out, fset1, pkg); err != nil { |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 313 | t.Fatal(err) |
| 314 | } |
Alan Donovan | ad74ff6 | 2023-05-11 18:07:57 -0400 | [diff] [blame] | 315 | exportdata := out.Bytes() |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 316 | |
| 317 | // import |
| 318 | imports := make(map[string]*types.Package) |
| 319 | fset2 := token.NewFileSet() |
Alan Donovan | ad74ff6 | 2023-05-11 18:07:57 -0400 | [diff] [blame] | 320 | _, pkg2, err := gcimporter.IImportData(fset2, imports, exportdata, pkg.Path()) |
Rebecca Stambler | 5c6ccfd | 2020-05-26 19:37:44 +0000 | [diff] [blame] | 321 | 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 | |
| 334 | const src = ` |
| 335 | package p |
| 336 | |
| 337 | type ( |
| 338 | T0 = int32 |
| 339 | T1 = struct{} |
| 340 | T2 = struct{ T1 } |
| 341 | Invalid = foo // foo is undeclared |
| 342 | ) |
| 343 | ` |
| 344 | |
| 345 | func 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 | } |