golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/ssa/builder_generic_test.go (about) 1 // Copyright 2022 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 ssa_test 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/parser" 11 "go/token" 12 "reflect" 13 "sort" 14 "testing" 15 16 "golang.org/x/tools/go/expect" 17 "golang.org/x/tools/go/loader" 18 "golang.org/x/tools/go/ssa" 19 ) 20 21 // TestGenericBodies tests that bodies of generic functions and methods containing 22 // different constructs can be built in BuilderMode(0). 23 // 24 // Each test specifies the contents of package containing a single go file. 25 // Each call print(arg0, arg1, ...) to the builtin print function 26 // in ssa is correlated a comment at the end of the line of the form: 27 // 28 // //@ types(a, b, c) 29 // 30 // where a, b and c are the types of the arguments to the print call 31 // serialized using go/types.Type.String(). 32 // See x/tools/go/expect for details on the syntax. 33 func TestGenericBodies(t *testing.T) { 34 for _, contents := range []string{ 35 ` 36 package p00 37 38 func f(x int) { 39 var i interface{} 40 print(i, 0) //@ types("interface{}", int) 41 print() //@ types() 42 print(x) //@ types(int) 43 } 44 `, 45 ` 46 package p01 47 48 func f[T any](x T) { 49 print(x) //@ types(T) 50 } 51 `, 52 ` 53 package p02 54 55 func f[T ~int]() { 56 var x T 57 print(x) //@ types(T) 58 } 59 `, 60 ` 61 package p03 62 63 func a[T ~[4]byte](x T) { 64 for k, v := range x { 65 print(x, k, v) //@ types(T, int, byte) 66 } 67 } 68 func b[T ~*[4]byte](x T) { 69 for k, v := range x { 70 print(x, k, v) //@ types(T, int, byte) 71 } 72 } 73 func c[T ~[]byte](x T) { 74 for k, v := range x { 75 print(x, k, v) //@ types(T, int, byte) 76 } 77 } 78 func d[T ~string](x T) { 79 for k, v := range x { 80 print(x, k, v) //@ types(T, int, rune) 81 } 82 } 83 func e[T ~map[int]string](x T) { 84 for k, v := range x { 85 print(x, k, v) //@ types(T, int, string) 86 } 87 } 88 func f[T ~chan string](x T) { 89 for v := range x { 90 print(x, v) //@ types(T, string) 91 } 92 } 93 94 func From() { 95 type A [4]byte 96 print(a[A]) //@ types("func(x p03.A)") 97 98 type B *[4]byte 99 print(b[B]) //@ types("func(x p03.B)") 100 101 type C []byte 102 print(c[C]) //@ types("func(x p03.C)") 103 104 type D string 105 print(d[D]) //@ types("func(x p03.D)") 106 107 type E map[int]string 108 print(e[E]) //@ types("func(x p03.E)") 109 110 type F chan string 111 print(f[F]) //@ types("func(x p03.F)") 112 } 113 `, 114 ` 115 package p05 116 117 func f[S any, T ~chan S](x T) { 118 for v := range x { 119 print(x, v) //@ types(T, S) 120 } 121 } 122 123 func From() { 124 type F chan string 125 print(f[string, F]) //@ types("func(x p05.F)") 126 } 127 `, 128 ` 129 package p06 130 131 func fibonacci[T ~chan int](c, quit T) { 132 x, y := 0, 1 133 for { 134 select { 135 case c <- x: 136 x, y = y, x+y 137 case <-quit: 138 print(c, quit, x, y) //@ types(T, T, int, int) 139 return 140 } 141 } 142 } 143 func start[T ~chan int](c, quit T) { 144 go func() { 145 for i := 0; i < 10; i++ { 146 print(<-c) //@ types(int) 147 } 148 quit <- 0 149 }() 150 } 151 func From() { 152 type F chan int 153 c := make(F) 154 quit := make(F) 155 print(start[F], c, quit) //@ types("func(c p06.F, quit p06.F)", "p06.F", "p06.F") 156 print(fibonacci[F], c, quit) //@ types("func(c p06.F, quit p06.F)", "p06.F", "p06.F") 157 } 158 `, 159 ` 160 package p07 161 162 func f[T ~struct{ x int; y string }](i int) T { 163 u := []T{ T{0, "lorem"}, T{1, "ipsum"}} 164 return u[i] 165 } 166 func From() { 167 type S struct{ x int; y string } 168 print(f[S]) //@ types("func(i int) p07.S") 169 } 170 `, 171 ` 172 package p08 173 174 func f[T ~[4]int8](x T, l, h int) []int8 { 175 return x[l:h] 176 } 177 func g[T ~*[4]int16](x T, l, h int) []int16 { 178 return x[l:h] 179 } 180 func h[T ~[]int32](x T, l, h int) T { 181 return x[l:h] 182 } 183 func From() { 184 type F [4]int8 185 type G *[4]int16 186 type H []int32 187 print(f[F](F{}, 0, 0)) //@ types("[]int8") 188 print(g[G](nil, 0, 0)) //@ types("[]int16") 189 print(h[H](nil, 0, 0)) //@ types("p08.H") 190 } 191 `, 192 ` 193 package p09 194 195 func h[E any, T ~[]E](x T, l, h int) []E { 196 s := x[l:h] 197 print(s) //@ types("T") 198 return s 199 } 200 func From() { 201 type H []int32 202 print(h[int32, H](nil, 0, 0)) //@ types("[]int32") 203 } 204 `, 205 ` 206 package p10 207 208 // Test "make" builtin with different forms on core types and 209 // when capacities are constants or variable. 210 func h[E any, T ~[]E](m, n int) { 211 print(make(T, 3)) //@ types(T) 212 print(make(T, 3, 5)) //@ types(T) 213 print(make(T, m)) //@ types(T) 214 print(make(T, m, n)) //@ types(T) 215 } 216 func i[K comparable, E any, T ~map[K]E](m int) { 217 print(make(T)) //@ types(T) 218 print(make(T, 5)) //@ types(T) 219 print(make(T, m)) //@ types(T) 220 } 221 func j[E any, T ~chan E](m int) { 222 print(make(T)) //@ types(T) 223 print(make(T, 6)) //@ types(T) 224 print(make(T, m)) //@ types(T) 225 } 226 func From() { 227 type H []int32 228 h[int32, H](3, 4) 229 type I map[int8]H 230 i[int8, H, I](5) 231 type J chan I 232 j[I, J](6) 233 } 234 `, 235 ` 236 package p11 237 238 func h[T ~[4]int](x T) { 239 print(len(x), cap(x)) //@ types(int, int) 240 } 241 func i[T ~[4]byte | []int | ~chan uint8](x T) { 242 print(len(x), cap(x)) //@ types(int, int) 243 } 244 func j[T ~[4]int | any | map[string]int]() { 245 print(new(T)) //@ types("*T") 246 } 247 func k[T ~[4]int | any | map[string]int](x T) { 248 print(x) //@ types(T) 249 panic(x) 250 } 251 `, 252 ` 253 package p12 254 255 func f[E any, F ~func() E](x F) { 256 print(x, x()) //@ types(F, E) 257 } 258 func From() { 259 type T func() int 260 f[int, T](func() int { return 0 }) 261 f[int, func() int](func() int { return 1 }) 262 } 263 `, 264 ` 265 package p13 266 267 func f[E any, M ~map[string]E](m M) { 268 y, ok := m["lorem"] 269 print(m, y, ok) //@ types(M, E, bool) 270 } 271 func From() { 272 type O map[string][]int 273 f(O{"lorem": []int{0, 1, 2, 3}}) 274 } 275 `, 276 ` 277 package p14 278 279 func a[T interface{ []int64 | [5]int64 }](x T) int64 { 280 print(x, x[2], x[3]) //@ types(T, int64, int64) 281 x[2] = 5 282 return x[3] 283 } 284 func b[T interface{ []byte | string }](x T) byte { 285 print(x, x[3]) //@ types(T, byte) 286 return x[3] 287 } 288 func c[T interface{ []byte }](x T) byte { 289 print(x, x[2], x[3]) //@ types(T, byte, byte) 290 x[2] = 'b' 291 return x[3] 292 } 293 func d[T interface{ map[int]int64 }](x T) int64 { 294 print(x, x[2], x[3]) //@ types(T, int64, int64) 295 x[2] = 43 296 return x[3] 297 } 298 func e[T ~string](t T) { 299 print(t, t[0]) //@ types(T, uint8) 300 } 301 func f[T ~string|[]byte](t T) { 302 print(t, t[0]) //@ types(T, uint8) 303 } 304 func g[T []byte](t T) { 305 print(t, t[0]) //@ types(T, byte) 306 } 307 func h[T ~[4]int|[]int](t T) { 308 print(t, t[0]) //@ types(T, int) 309 } 310 func i[T ~[4]int|*[4]int|[]int](t T) { 311 print(t, t[0]) //@ types(T, int) 312 } 313 func j[T ~[4]int|*[4]int|[]int](t T) { 314 print(t, &t[0]) //@ types(T, "*int") 315 } 316 `, 317 ` 318 package p15 319 320 type MyInt int 321 type Other int 322 type MyInterface interface{ foo() } 323 324 // ChangeType tests 325 func ct0(x int) { v := MyInt(x); print(x, v) /*@ types(int, "p15.MyInt")*/ } 326 func ct1[T MyInt | Other, S int ](x S) { v := T(x); print(x, v) /*@ types(S, T)*/ } 327 func ct2[T int, S MyInt | int ](x S) { v := T(x); print(x, v) /*@ types(S, T)*/ } 328 func ct3[T MyInt | Other, S MyInt | int ](x S) { v := T(x) ; print(x, v) /*@ types(S, T)*/ } 329 330 // Convert tests 331 func co0[T int | int8](x MyInt) { v := T(x); print(x, v) /*@ types("p15.MyInt", T)*/} 332 func co1[T int | int8](x T) { v := MyInt(x); print(x, v) /*@ types(T, "p15.MyInt")*/ } 333 func co2[S, T int | int8](x T) { v := S(x); print(x, v) /*@ types(T, S)*/ } 334 335 // MakeInterface tests 336 func mi0[T MyInterface](x T) { v := MyInterface(x); print(x, v) /*@ types(T, "p15.MyInterface")*/ } 337 338 // NewConst tests 339 func nc0[T any]() { v := (*T)(nil); print(v) /*@ types("*T")*/} 340 341 // SliceToArrayPointer 342 func sl0[T *[4]int | *[2]int](x []int) { v := T(x); print(x, v) /*@ types("[]int", T)*/ } 343 func sl1[T *[4]int | *[2]int, S []int](x S) { v := T(x); print(x, v) /*@ types(S, T)*/ } 344 `, 345 ` 346 package p16 347 348 func c[T interface{ foo() string }](x T) { 349 print(x, x.foo, x.foo()) /*@ types(T, "func() string", string)*/ 350 } 351 `, 352 ` 353 package p17 354 355 func eq[T comparable](t T, i interface{}) bool { 356 return t == i 357 } 358 `, 359 // TODO(59983): investigate why writing g.c panics in (*FieldAddr).String. 360 ` 361 package p18 362 363 type S struct{ f int } 364 func c[P *S]() []P { return []P{{f: 1}} } 365 `, 366 ` 367 package p19 368 369 func sign[bytes []byte | string](s bytes) (bool, bool) { 370 neg := false 371 if len(s) > 0 && (s[0] == '-' || s[0] == '+') { 372 neg = s[0] == '-' 373 s = s[1:] 374 } 375 return !neg, len(s) > 0 376 } 377 `, 378 `package p20 379 380 func digits[bytes []byte | string](s bytes) bool { 381 for _, c := range []byte(s) { 382 if c < '0' || '9' < c { 383 return false 384 } 385 } 386 return true 387 } 388 `, 389 ` 390 package p21 391 392 type E interface{} 393 394 func Foo[T E, PT interface{ *T }]() T { 395 pt := PT(new(T)) 396 x := *pt 397 print(x) /*@ types(T)*/ 398 return x 399 } 400 `, 401 ` 402 package p22 403 404 func f[M any, PM *M](p PM) { 405 var m M 406 *p = m 407 print(m) /*@ types(M)*/ 408 print(p) /*@ types(PM)*/ 409 } 410 `, 411 ` 412 package p23 413 414 type A struct{int} 415 func (*A) Marker() {} 416 417 type B struct{string} 418 func (*B) Marker() {} 419 420 type C struct{float32} 421 func (*C) Marker() {} 422 423 func process[T interface { 424 *A 425 *B 426 *C 427 Marker() 428 }](v T) { 429 v.Marker() 430 a := *(any(v).(*A)); print(a) /*@ types("p23.A")*/ 431 b := *(any(v).(*B)); print(b) /*@ types("p23.B")*/ 432 c := *(any(v).(*C)); print(c) /*@ types("p23.C")*/ 433 } 434 `, 435 ` 436 package p24 437 438 func a[T any](f func() [4]T) { 439 x := len(f()) 440 print(x) /*@ types("int")*/ 441 } 442 443 func b[T [4]any](f func() T) { 444 x := len(f()) 445 print(x) /*@ types("int")*/ 446 } 447 448 func c[T any](f func() *[4]T) { 449 x := len(f()) 450 print(x) /*@ types("int")*/ 451 } 452 453 func d[T *[4]any](f func() T) { 454 x := len(f()) 455 print(x) /*@ types("int")*/ 456 } 457 `, 458 ` 459 package p25 460 461 func a[T any]() { 462 var f func() [4]T 463 for i, v := range f() { 464 print(i, v) /*@ types("int", "T")*/ 465 } 466 } 467 468 func b[T [4]any](f func() T) { 469 for i, v := range f() { 470 print(i, v) /*@ types("int", "any")*/ 471 } 472 } 473 474 func c[T any](f func() *[4]T) { 475 for i, v := range f() { 476 print(i, v) /*@ types("int", "T")*/ 477 } 478 } 479 480 func d[T *[4]any](f func() T) { 481 for i, v := range f() { 482 print(i, v) /*@ types("int", "any")*/ 483 } 484 } 485 `, 486 ` 487 package issue64324 488 489 type bar[T any] interface { 490 Bar(int) T 491 } 492 type foo[T any] interface { 493 bar[[]T] 494 *T 495 } 496 func Foo[T any, F foo[T]](d int) { 497 m := new(T) 498 f := F(m) 499 print(f.Bar(d)) /*@ types("[]T")*/ 500 } 501 `, ` 502 package issue64324b 503 504 type bar[T any] interface { 505 Bar(int) T 506 } 507 type baz[T any] interface { 508 bar[*int] 509 *int 510 } 511 512 func Baz[I baz[string]](d int) { 513 m := new(int) 514 f := I(m) 515 print(f.Bar(d)) /*@ types("*int")*/ 516 } 517 `, 518 } { 519 contents := contents 520 pkgname := packageName(t, contents) 521 t.Run(pkgname, func(t *testing.T) { 522 // Parse 523 conf := loader.Config{ParserMode: parser.ParseComments} 524 f, err := conf.ParseFile("file.go", contents) 525 if err != nil { 526 t.Fatalf("parse: %v", err) 527 } 528 conf.CreateFromFiles(pkgname, f) 529 530 // Load 531 lprog, err := conf.Load() 532 if err != nil { 533 t.Fatalf("Load: %v", err) 534 } 535 536 // Create and build SSA 537 prog := ssa.NewProgram(lprog.Fset, ssa.SanityCheckFunctions) 538 for _, info := range lprog.AllPackages { 539 if info.TransitivelyErrorFree { 540 prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable) 541 } 542 } 543 p := prog.Package(lprog.Package(pkgname).Pkg) 544 p.Build() 545 546 // Collect all notes in f, i.e. comments starting with "//@ types". 547 notes, err := expect.ExtractGo(prog.Fset, f) 548 if err != nil { 549 t.Errorf("expect.ExtractGo: %v", err) 550 } 551 552 // Collect calls to the builtin print function. 553 probes := callsTo(p, "print") 554 expectations := matchNotes(prog.Fset, notes, probes) 555 556 for call := range probes { 557 if expectations[call] == nil { 558 t.Errorf("Unmatched call: %v", call) 559 } 560 } 561 562 // Check each expectation. 563 for call, note := range expectations { 564 var args []string 565 for _, a := range call.Args { 566 args = append(args, a.Type().String()) 567 } 568 if got, want := fmt.Sprint(args), fmt.Sprint(note.Args); got != want { 569 t.Errorf("Arguments to print() were expected to be %q. got %q", want, got) 570 logFunction(t, probes[call]) 571 } 572 } 573 }) 574 } 575 } 576 577 // callsTo finds all calls to an SSA value named fname, 578 // and returns a map from each call site to its enclosing function. 579 func callsTo(p *ssa.Package, fname string) map[*ssa.CallCommon]*ssa.Function { 580 callsites := make(map[*ssa.CallCommon]*ssa.Function) 581 for _, mem := range p.Members { 582 if fn, ok := mem.(*ssa.Function); ok { 583 for _, bb := range fn.Blocks { 584 for _, i := range bb.Instrs { 585 if i, ok := i.(ssa.CallInstruction); ok { 586 call := i.Common() 587 if call.Value.Name() == fname { 588 callsites[call] = fn 589 } 590 } 591 } 592 } 593 } 594 } 595 return callsites 596 } 597 598 // matchNotes returns a mapping from call sites (found by callsTo) 599 // to the first "//@ note" comment on the same line. 600 func matchNotes(fset *token.FileSet, notes []*expect.Note, calls map[*ssa.CallCommon]*ssa.Function) map[*ssa.CallCommon]*expect.Note { 601 // Matches each probe with a note that has the same line. 602 sameLine := func(x, y token.Pos) bool { 603 xp := fset.Position(x) 604 yp := fset.Position(y) 605 return xp.Filename == yp.Filename && xp.Line == yp.Line 606 } 607 expectations := make(map[*ssa.CallCommon]*expect.Note) 608 for call := range calls { 609 for _, note := range notes { 610 if sameLine(call.Pos(), note.Pos) { 611 expectations[call] = note 612 break // first match is good enough. 613 } 614 } 615 } 616 return expectations 617 } 618 619 // TestInstructionString tests serializing instructions via Instruction.String(). 620 func TestInstructionString(t *testing.T) { 621 // Tests (ssa.Instruction).String(). Instructions are from a single go file. 622 // The Instructions tested are those that match a comment of the form: 623 // 624 // //@ instrs(f, kind, strs...) 625 // 626 // where f is the name of the function, kind is the type of the instructions matched 627 // within the function, and tests that the String() value for all of the instructions 628 // matched of String() is strs (in some order). 629 // See x/tools/go/expect for details on the syntax. 630 631 const contents = ` 632 package p 633 634 //@ instrs("f0", "*ssa.TypeAssert") 635 //@ instrs("f0", "*ssa.Call", "print(nil:interface{}, 0:int)") 636 func f0(x int) { // non-generic smoke test. 637 var i interface{} 638 print(i, 0) 639 } 640 641 //@ instrs("f1", "*ssa.Alloc", "local T (u)") 642 //@ instrs("f1", "*ssa.FieldAddr", "&t0.x [#0]") 643 func f1[T ~struct{ x string }]() T { 644 u := T{"lorem"} 645 return u 646 } 647 648 //@ instrs("f1b", "*ssa.Alloc", "new T (complit)") 649 //@ instrs("f1b", "*ssa.FieldAddr", "&t0.x [#0]") 650 func f1b[T ~struct{ x string }]() *T { 651 u := &T{"lorem"} 652 return u 653 } 654 655 //@ instrs("f2", "*ssa.TypeAssert", "typeassert t0.(interface{})") 656 //@ instrs("f2", "*ssa.Call", "invoke x.foo()") 657 func f2[T interface{ foo() string }](x T) { 658 _ = x.foo 659 _ = x.foo() 660 } 661 662 //@ instrs("f3", "*ssa.TypeAssert", "typeassert t0.(interface{})") 663 //@ instrs("f3", "*ssa.Call", "invoke x.foo()") 664 func f3[T interface{ foo() string; comparable }](x T) { 665 _ = x.foo 666 _ = x.foo() 667 } 668 669 //@ instrs("f4", "*ssa.BinOp", "t1 + 1:int", "t2 < 4:int") 670 //@ instrs("f4", "*ssa.Call", "f()", "print(t2, t4)") 671 func f4[T [4]string](f func() T) { 672 for i, v := range f() { 673 print(i, v) 674 } 675 } 676 677 //@ instrs("f5", "*ssa.Call", "nil:func()()") 678 func f5() { 679 var f func() 680 f() 681 } 682 683 type S struct{ f int } 684 685 //@ instrs("f6", "*ssa.Alloc", "new [1]P (slicelit)", "new S (complit)") 686 //@ instrs("f6", "*ssa.IndexAddr", "&t0[0:int]") 687 //@ instrs("f6", "*ssa.FieldAddr", "&t2.f [#0]") 688 func f6[P *S]() []P { return []P{{f: 1}} } 689 690 //@ instrs("f7", "*ssa.Alloc", "local S (complit)") 691 //@ instrs("f7", "*ssa.FieldAddr", "&t0.f [#0]") 692 func f7[T any, S struct{f T}](x T) S { return S{f: x} } 693 694 //@ instrs("f8", "*ssa.Alloc", "new [1]P (slicelit)", "new struct{f T} (complit)") 695 //@ instrs("f8", "*ssa.IndexAddr", "&t0[0:int]") 696 //@ instrs("f8", "*ssa.FieldAddr", "&t2.f [#0]") 697 func f8[T any, P *struct{f T}](x T) []P { return []P{{f: x}} } 698 699 //@ instrs("f9", "*ssa.Alloc", "new [1]PS (slicelit)", "new S (complit)") 700 //@ instrs("f9", "*ssa.IndexAddr", "&t0[0:int]") 701 //@ instrs("f9", "*ssa.FieldAddr", "&t2.f [#0]") 702 func f9[T any, S struct{f T}, PS *S](x T) { 703 _ = []PS{{f: x}} 704 } 705 706 //@ instrs("f10", "*ssa.FieldAddr", "&t0.x [#0]") 707 //@ instrs("f10", "*ssa.Store", "*t0 = *new(T):T", "*t1 = 4:int") 708 func f10[T ~struct{ x, y int }]() T { 709 var u T 710 u = T{x: 4} 711 return u 712 } 713 714 //@ instrs("f11", "*ssa.FieldAddr", "&t1.y [#1]") 715 //@ instrs("f11", "*ssa.Store", "*t1 = *new(T):T", "*t2 = 5:int") 716 func f11[T ~struct{ x, y int }, PT *T]() PT { 717 var u PT = new(T) 718 *u = T{y: 5} 719 return u 720 } 721 722 //@ instrs("f12", "*ssa.Alloc", "new struct{f T} (complit)") 723 //@ instrs("f12", "*ssa.MakeMap", "make map[P]bool 1:int") 724 func f12[T any, P *struct{f T}](x T) map[P]bool { return map[P]bool{{}: true} } 725 726 //@ instrs("f13", "*ssa.IndexAddr", "&v[0:int]") 727 //@ instrs("f13", "*ssa.Store", "*t0 = 7:int", "*v = *new(A):A") 728 func f13[A [3]int, PA *A](v PA) { 729 *v = A{7} 730 } 731 732 //@ instrs("f14", "*ssa.Call", "invoke t1.Set(0:int)") 733 func f14[T any, PT interface { 734 Set(int) 735 *T 736 }]() { 737 var t T 738 p := PT(&t) 739 p.Set(0) 740 } 741 742 //@ instrs("f15", "*ssa.MakeClosure", "make closure (interface{Set(int); *T}).Set$bound [t1]") 743 func f15[T any, PT interface { 744 Set(int) 745 *T 746 }]() func(int) { 747 var t T 748 p := PT(&t) 749 return p.Set 750 } 751 ` 752 753 // Parse 754 conf := loader.Config{ParserMode: parser.ParseComments} 755 const fname = "p.go" 756 f, err := conf.ParseFile(fname, contents) 757 if err != nil { 758 t.Fatalf("parse: %v", err) 759 } 760 conf.CreateFromFiles("p", f) 761 762 // Load 763 lprog, err := conf.Load() 764 if err != nil { 765 t.Fatalf("Load: %v", err) 766 } 767 768 // Create and build SSA 769 prog := ssa.NewProgram(lprog.Fset, ssa.SanityCheckFunctions) 770 for _, info := range lprog.AllPackages { 771 if info.TransitivelyErrorFree { 772 prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable) 773 } 774 } 775 p := prog.Package(lprog.Package("p").Pkg) 776 p.Build() 777 778 // Collect all notes in f, i.e. comments starting with "//@ instr". 779 notes, err := expect.ExtractGo(prog.Fset, f) 780 if err != nil { 781 t.Errorf("expect.ExtractGo: %v", err) 782 } 783 784 // Expectation is a {function, type string} -> {want, matches} 785 // where matches is all Instructions.String() that match the key. 786 // Each expecation is that some permutation of matches is wants. 787 type expKey struct { 788 function string 789 kind string 790 } 791 type expValue struct { 792 wants []string 793 matches []string 794 } 795 expectations := make(map[expKey]*expValue) 796 for _, note := range notes { 797 if note.Name == "instrs" { 798 if len(note.Args) < 2 { 799 t.Error("Had @instrs annotation without at least 2 arguments") 800 continue 801 } 802 fn, kind := fmt.Sprint(note.Args[0]), fmt.Sprint(note.Args[1]) 803 var wants []string 804 for _, arg := range note.Args[2:] { 805 wants = append(wants, fmt.Sprint(arg)) 806 } 807 expectations[expKey{fn, kind}] = &expValue{wants, nil} 808 } 809 } 810 811 // Collect all Instructions that match the expectations. 812 for _, mem := range p.Members { 813 if fn, ok := mem.(*ssa.Function); ok { 814 for _, bb := range fn.Blocks { 815 for _, i := range bb.Instrs { 816 kind := fmt.Sprintf("%T", i) 817 if e := expectations[expKey{fn.Name(), kind}]; e != nil { 818 e.matches = append(e.matches, i.String()) 819 } 820 } 821 } 822 } 823 } 824 825 // Check each expectation. 826 for key, value := range expectations { 827 fn, ok := p.Members[key.function].(*ssa.Function) 828 if !ok { 829 t.Errorf("Expectation on %s does not match a member in %s", key.function, p.Pkg.Name()) 830 } 831 got, want := value.matches, value.wants 832 sort.Strings(got) 833 sort.Strings(want) 834 if !reflect.DeepEqual(want, got) { 835 t.Errorf("Within %s wanted instructions of kind %s: %q. got %q", key.function, key.kind, want, got) 836 logFunction(t, fn) 837 } 838 } 839 } 840 841 // packageName is a test helper to extract the package name from a string 842 // containing the content of a go file. 843 func packageName(t testing.TB, content string) string { 844 f, err := parser.ParseFile(token.NewFileSet(), "", content, parser.PackageClauseOnly) 845 if err != nil { 846 t.Fatalf("parsing the file %q failed with error: %s", content, err) 847 } 848 return f.Name.Name 849 } 850 851 func logFunction(t testing.TB, fn *ssa.Function) { 852 // TODO: Consider adding a ssa.Function.GoString() so this can be logged to t via '%#v'. 853 var buf bytes.Buffer 854 ssa.WriteFunction(&buf, fn) 855 t.Log(buf.String()) 856 }