| 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 | "fmt" |
| 9 | "go/parser" |
| 10 | "go/token" |
| 11 | "reflect" |
| 12 | "sort" |
| 13 | "testing" |
| 14 | |
| 15 | "golang.org/x/tools/go/expect" |
| 16 | "golang.org/x/tools/go/loader" |
| 17 | "golang.org/x/tools/go/ssa" |
| 18 | "golang.org/x/tools/internal/typeparams" |
| 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 | if !typeparams.Enabled { |
| 35 | t.Skip("TestGenericBodies requires type parameters") |
| 36 | } |
| 37 | for _, test := range []struct { |
| 38 | pkg string // name of the package. |
| 39 | contents string // contents of the Go package. |
| 40 | }{ |
| 41 | { |
| 42 | pkg: "p", |
| 43 | contents: ` |
| 44 | package p |
| 45 | |
| 46 | func f(x int) { |
| 47 | var i interface{} |
| 48 | print(i, 0) //@ types("interface{}", int) |
| 49 | print() //@ types() |
| 50 | print(x) //@ types(int) |
| 51 | } |
| 52 | `, |
| 53 | }, |
| 54 | { |
| 55 | pkg: "q", |
| 56 | contents: ` |
| 57 | package q |
| 58 | |
| 59 | func f[T any](x T) { |
| 60 | print(x) //@ types(T) |
| 61 | } |
| 62 | `, |
| 63 | }, |
| 64 | { |
| 65 | pkg: "r", |
| 66 | contents: ` |
| 67 | package r |
| 68 | |
| 69 | func f[T ~int]() { |
| 70 | var x T |
| 71 | print(x) //@ types(T) |
| 72 | } |
| 73 | `, |
| 74 | }, |
| 75 | { |
| 76 | pkg: "s", |
| 77 | contents: ` |
| 78 | package s |
| 79 | |
| 80 | func a[T ~[4]byte](x T) { |
| 81 | for k, v := range x { |
| 82 | print(x, k, v) //@ types(T, int, byte) |
| 83 | } |
| 84 | } |
| 85 | func b[T ~*[4]byte](x T) { |
| 86 | for k, v := range x { |
| 87 | print(x, k, v) //@ types(T, int, byte) |
| 88 | } |
| 89 | } |
| 90 | func c[T ~[]byte](x T) { |
| 91 | for k, v := range x { |
| 92 | print(x, k, v) //@ types(T, int, byte) |
| 93 | } |
| 94 | } |
| 95 | func d[T ~string](x T) { |
| 96 | for k, v := range x { |
| 97 | print(x, k, v) //@ types(T, int, rune) |
| 98 | } |
| 99 | } |
| 100 | func e[T ~map[int]string](x T) { |
| 101 | for k, v := range x { |
| 102 | print(x, k, v) //@ types(T, int, string) |
| 103 | } |
| 104 | } |
| 105 | func f[T ~chan string](x T) { |
| 106 | for v := range x { |
| 107 | print(x, v) //@ types(T, string) |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | func From() { |
| 112 | type A [4]byte |
| 113 | print(a[A]) //@ types("func(x s.A)") |
| 114 | |
| 115 | type B *[4]byte |
| 116 | print(b[B]) //@ types("func(x s.B)") |
| 117 | |
| 118 | type C []byte |
| 119 | print(c[C]) //@ types("func(x s.C)") |
| 120 | |
| 121 | type D string |
| 122 | print(d[D]) //@ types("func(x s.D)") |
| 123 | |
| 124 | type E map[int]string |
| 125 | print(e[E]) //@ types("func(x s.E)") |
| 126 | |
| 127 | type F chan string |
| 128 | print(f[F]) //@ types("func(x s.F)") |
| 129 | } |
| 130 | `, |
| 131 | }, |
| 132 | { |
| 133 | pkg: "t", |
| 134 | contents: ` |
| 135 | package t |
| 136 | |
| 137 | func f[S any, T ~chan S](x T) { |
| 138 | for v := range x { |
| 139 | print(x, v) //@ types(T, S) |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | func From() { |
| 144 | type F chan string |
| 145 | print(f[string, F]) //@ types("func(x t.F)") |
| 146 | } |
| 147 | `, |
| 148 | }, |
| 149 | { |
| 150 | pkg: "u", |
| 151 | contents: ` |
| 152 | package u |
| 153 | |
| 154 | func fibonacci[T ~chan int](c, quit T) { |
| 155 | x, y := 0, 1 |
| 156 | for { |
| 157 | select { |
| 158 | case c <- x: |
| 159 | x, y = y, x+y |
| 160 | case <-quit: |
| 161 | print(c, quit, x, y) //@ types(T, T, int, int) |
| 162 | return |
| 163 | } |
| 164 | } |
| 165 | } |
| 166 | func start[T ~chan int](c, quit T) { |
| 167 | go func() { |
| 168 | for i := 0; i < 10; i++ { |
| 169 | print(<-c) //@ types(int) |
| 170 | } |
| 171 | quit <- 0 |
| 172 | }() |
| 173 | } |
| 174 | func From() { |
| 175 | type F chan int |
| 176 | c := make(F) |
| 177 | quit := make(F) |
| 178 | print(start[F], c, quit) //@ types("func(c u.F, quit u.F)", "u.F", "u.F") |
| 179 | print(fibonacci[F], c, quit) //@ types("func(c u.F, quit u.F)", "u.F", "u.F") |
| 180 | } |
| 181 | `, |
| 182 | }, |
| 183 | { |
| 184 | pkg: "v", |
| 185 | contents: ` |
| 186 | package v |
| 187 | |
| 188 | func f[T ~struct{ x int; y string }](i int) T { |
| 189 | u := []T{ T{0, "lorem"}, T{1, "ipsum"}} |
| 190 | return u[i] |
| 191 | } |
| 192 | func From() { |
| 193 | type S struct{ x int; y string } |
| 194 | print(f[S]) //@ types("func(i int) v.S") |
| 195 | } |
| 196 | `, |
| 197 | }, |
| 198 | { |
| 199 | pkg: "w", |
| 200 | contents: ` |
| 201 | package w |
| 202 | |
| 203 | func f[T ~[4]int8](x T, l, h int) []int8 { |
| 204 | return x[l:h] |
| 205 | } |
| 206 | func g[T ~*[4]int16](x T, l, h int) []int16 { |
| 207 | return x[l:h] |
| 208 | } |
| 209 | func h[T ~[]int32](x T, l, h int) T { |
| 210 | return x[l:h] |
| 211 | } |
| 212 | func From() { |
| 213 | type F [4]int8 |
| 214 | type G *[4]int16 |
| 215 | type H []int32 |
| 216 | print(f[F](F{}, 0, 0)) //@ types("[]int8") |
| 217 | print(g[G](nil, 0, 0)) //@ types("[]int16") |
| 218 | print(h[H](nil, 0, 0)) //@ types("w.H") |
| 219 | } |
| 220 | `, |
| 221 | }, |
| 222 | { |
| 223 | pkg: "x", |
| 224 | contents: ` |
| 225 | package x |
| 226 | |
| 227 | func h[E any, T ~[]E](x T, l, h int) []E { |
| 228 | s := x[l:h] |
| 229 | print(s) //@ types("T") |
| 230 | return s |
| 231 | } |
| 232 | func From() { |
| 233 | type H []int32 |
| 234 | print(h[int32, H](nil, 0, 0)) //@ types("[]int32") |
| 235 | } |
| 236 | `, |
| 237 | }, |
| 238 | { |
| 239 | pkg: "y", |
| 240 | contents: ` |
| 241 | package y |
| 242 | |
| 243 | // Test "make" builtin with different forms on core types and |
| 244 | // when capacities are constants or variable. |
| 245 | func h[E any, T ~[]E](m, n int) { |
| 246 | print(make(T, 3)) //@ types(T) |
| 247 | print(make(T, 3, 5)) //@ types(T) |
| 248 | print(make(T, m)) //@ types(T) |
| 249 | print(make(T, m, n)) //@ types(T) |
| 250 | } |
| 251 | func i[K comparable, E any, T ~map[K]E](m int) { |
| 252 | print(make(T)) //@ types(T) |
| 253 | print(make(T, 5)) //@ types(T) |
| 254 | print(make(T, m)) //@ types(T) |
| 255 | } |
| 256 | func j[E any, T ~chan E](m int) { |
| 257 | print(make(T)) //@ types(T) |
| 258 | print(make(T, 6)) //@ types(T) |
| 259 | print(make(T, m)) //@ types(T) |
| 260 | } |
| 261 | func From() { |
| 262 | type H []int32 |
| 263 | h[int32, H](3, 4) |
| 264 | type I map[int8]H |
| 265 | i[int8, H, I](5) |
| 266 | type J chan I |
| 267 | j[I, J](6) |
| 268 | } |
| 269 | `, |
| 270 | }, |
| 271 | { |
| 272 | pkg: "z", |
| 273 | contents: ` |
| 274 | package z |
| 275 | |
| 276 | func h[T ~[4]int](x T) { |
| 277 | print(len(x), cap(x)) //@ types(int, int) |
| 278 | } |
| 279 | func i[T ~[4]byte | []int | ~chan uint8](x T) { |
| 280 | print(len(x), cap(x)) //@ types(int, int) |
| 281 | } |
| 282 | func j[T ~[4]int | any | map[string]int]() { |
| 283 | print(new(T)) //@ types("*T") |
| 284 | } |
| 285 | func k[T ~[4]int | any | map[string]int](x T) { |
| 286 | print(x) //@ types(T) |
| 287 | panic(x) |
| 288 | } |
| 289 | `, |
| 290 | }, |
| 291 | { |
| 292 | pkg: "a", |
| 293 | contents: ` |
| 294 | package a |
| 295 | |
| 296 | func f[E any, F ~func() E](x F) { |
| 297 | print(x, x()) //@ types(F, E) |
| 298 | } |
| 299 | func From() { |
| 300 | type T func() int |
| 301 | f[int, T](func() int { return 0 }) |
| 302 | f[int, func() int](func() int { return 1 }) |
| 303 | } |
| 304 | `, |
| 305 | }, |
| 306 | { |
| 307 | pkg: "b", |
| 308 | contents: ` |
| 309 | package b |
| 310 | |
| 311 | func f[E any, M ~map[string]E](m M) { |
| 312 | y, ok := m["lorem"] |
| 313 | print(m, y, ok) //@ types(M, E, bool) |
| 314 | } |
| 315 | func From() { |
| 316 | type O map[string][]int |
| 317 | f(O{"lorem": []int{0, 1, 2, 3}}) |
| 318 | } |
| 319 | `, |
| 320 | }, |
| 321 | { |
| 322 | pkg: "c", |
| 323 | contents: ` |
| 324 | package c |
| 325 | |
| 326 | func a[T interface{ []int64 | [5]int64 }](x T) int64 { |
| 327 | print(x, x[2], x[3]) //@ types(T, int64, int64) |
| 328 | x[2] = 5 |
| 329 | return x[3] |
| 330 | } |
| 331 | func b[T interface{ []byte | string }](x T) byte { |
| 332 | print(x, x[3]) //@ types(T, byte) |
| 333 | return x[3] |
| 334 | } |
| 335 | func c[T interface{ []byte }](x T) byte { |
| 336 | print(x, x[2], x[3]) //@ types(T, byte, byte) |
| 337 | x[2] = 'b' |
| 338 | return x[3] |
| 339 | } |
| 340 | func d[T interface{ map[int]int64 }](x T) int64 { |
| 341 | print(x, x[2], x[3]) //@ types(T, int64, int64) |
| 342 | x[2] = 43 |
| 343 | return x[3] |
| 344 | } |
| 345 | func e[T ~string](t T) { |
| 346 | print(t, t[0]) //@ types(T, uint8) |
| 347 | } |
| 348 | func f[T ~string|[]byte](t T) { |
| 349 | print(t, t[0]) //@ types(T, uint8) |
| 350 | } |
| 351 | func g[T []byte](t T) { |
| 352 | print(t, t[0]) //@ types(T, byte) |
| 353 | } |
| 354 | func h[T ~[4]int|[]int](t T) { |
| 355 | print(t, t[0]) //@ types(T, int) |
| 356 | } |
| 357 | func i[T ~[4]int|*[4]int|[]int](t T) { |
| 358 | print(t, t[0]) //@ types(T, int) |
| 359 | } |
| 360 | func j[T ~[4]int|*[4]int|[]int](t T) { |
| 361 | print(t, &t[0]) //@ types(T, "*int") |
| 362 | } |
| 363 | `, |
| 364 | }, |
| 365 | { |
| 366 | pkg: "d", |
| 367 | contents: ` |
| 368 | package d |
| 369 | |
| 370 | type MyInt int |
| 371 | type Other int |
| 372 | type MyInterface interface{ foo() } |
| 373 | |
| 374 | // ChangeType tests |
| 375 | func ct0(x int) { v := MyInt(x); print(x, v) /*@ types(int, "d.MyInt")*/ } |
| 376 | func ct1[T MyInt | Other, S int ](x S) { v := T(x); print(x, v) /*@ types(S, T)*/ } |
| 377 | func ct2[T int, S MyInt | int ](x S) { v := T(x); print(x, v) /*@ types(S, T)*/ } |
| 378 | func ct3[T MyInt | Other, S MyInt | int ](x S) { v := T(x) ; print(x, v) /*@ types(S, T)*/ } |
| 379 | |
| 380 | // Convert tests |
| 381 | func co0[T int | int8](x MyInt) { v := T(x); print(x, v) /*@ types("d.MyInt", T)*/} |
| 382 | func co1[T int | int8](x T) { v := MyInt(x); print(x, v) /*@ types(T, "d.MyInt")*/ } |
| 383 | func co2[S, T int | int8](x T) { v := S(x); print(x, v) /*@ types(T, S)*/ } |
| 384 | |
| 385 | // MakeInterface tests |
| 386 | func mi0[T MyInterface](x T) { v := MyInterface(x); print(x, v) /*@ types(T, "d.MyInterface")*/ } |
| 387 | |
| 388 | // NewConst tests |
| 389 | func nc0[T any]() { v := (*T)(nil); print(v) /*@ types("*T")*/} |
| 390 | |
| 391 | // SliceToArrayPointer |
| 392 | func sl0[T *[4]int | *[2]int](x []int) { v := T(x); print(x, v) /*@ types("[]int", T)*/ } |
| 393 | func sl1[T *[4]int | *[2]int, S []int](x S) { v := T(x); print(x, v) /*@ types(S, T)*/ } |
| 394 | `, |
| 395 | }, |
| 396 | { |
| 397 | pkg: "e", |
| 398 | contents: ` |
| 399 | package e |
| 400 | |
| 401 | func c[T interface{ foo() string }](x T) { |
| 402 | print(x, x.foo, x.foo()) /*@ types(T, "func() string", string)*/ |
| 403 | } |
| 404 | `, |
| 405 | }, |
| 406 | { |
| 407 | pkg: "f", |
| 408 | contents: `package f |
| 409 | |
| 410 | func eq[T comparable](t T, i interface{}) bool { |
| 411 | return t == i |
| 412 | } |
| 413 | `, |
| 414 | }, |
| 415 | { |
| 416 | pkg: "g", |
| 417 | contents: `package g |
| 418 | type S struct{ f int } |
| 419 | func c[P *S]() []P { return []P{{f: 1}} } |
| 420 | `, |
| 421 | }, |
| 422 | { |
| 423 | pkg: "h", |
| 424 | contents: `package h |
| 425 | func sign[bytes []byte | string](s bytes) (bool, bool) { |
| 426 | neg := false |
| 427 | if len(s) > 0 && (s[0] == '-' || s[0] == '+') { |
| 428 | neg = s[0] == '-' |
| 429 | s = s[1:] |
| 430 | } |
| 431 | return !neg, len(s) > 0 |
| 432 | }`, |
| 433 | }, |
| 434 | { |
| 435 | pkg: "i", |
| 436 | contents: `package i |
| 437 | func digits[bytes []byte | string](s bytes) bool { |
| 438 | for _, c := range []byte(s) { |
| 439 | if c < '0' || '9' < c { |
| 440 | return false |
| 441 | } |
| 442 | } |
| 443 | return true |
| 444 | }`, |
| 445 | }, |
| 446 | { |
| 447 | pkg: "j", |
| 448 | contents: ` |
| 449 | package j |
| 450 | |
| 451 | type E interface{} |
| 452 | |
| 453 | func Foo[T E, PT interface{ *T }]() T { |
| 454 | pt := PT(new(T)) |
| 455 | x := *pt |
| 456 | print(x) /*@ types(T)*/ |
| 457 | return x |
| 458 | } |
| 459 | `, |
| 460 | }, |
| 461 | } { |
| 462 | test := test |
| 463 | t.Run(test.pkg, func(t *testing.T) { |
| 464 | // Parse |
| 465 | conf := loader.Config{ParserMode: parser.ParseComments} |
| 466 | fname := test.pkg + ".go" |
| 467 | f, err := conf.ParseFile(fname, test.contents) |
| 468 | if err != nil { |
| 469 | t.Fatalf("parse: %v", err) |
| 470 | } |
| 471 | conf.CreateFromFiles(test.pkg, f) |
| 472 | |
| 473 | // Load |
| 474 | lprog, err := conf.Load() |
| 475 | if err != nil { |
| 476 | t.Fatalf("Load: %v", err) |
| 477 | } |
| 478 | |
| 479 | // Create and build SSA |
| 480 | prog := ssa.NewProgram(lprog.Fset, ssa.SanityCheckFunctions) |
| 481 | for _, info := range lprog.AllPackages { |
| 482 | if info.TransitivelyErrorFree { |
| 483 | prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable) |
| 484 | } |
| 485 | } |
| 486 | p := prog.Package(lprog.Package(test.pkg).Pkg) |
| 487 | p.Build() |
| 488 | |
| 489 | // Collect calls to the builtin print function. |
| 490 | probes := make(map[*ssa.CallCommon]bool) |
| 491 | for _, mem := range p.Members { |
| 492 | if fn, ok := mem.(*ssa.Function); ok { |
| 493 | for _, bb := range fn.Blocks { |
| 494 | for _, i := range bb.Instrs { |
| 495 | if i, ok := i.(ssa.CallInstruction); ok { |
| 496 | call := i.Common() |
| 497 | if b, ok := call.Value.(*ssa.Builtin); ok && b.Name() == "print" { |
| 498 | probes[i.Common()] = true |
| 499 | } |
| 500 | } |
| 501 | } |
| 502 | } |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | // Collect all notes in f, i.e. comments starting with "//@ types". |
| 507 | notes, err := expect.ExtractGo(prog.Fset, f) |
| 508 | if err != nil { |
| 509 | t.Errorf("expect.ExtractGo: %v", err) |
| 510 | } |
| 511 | |
| 512 | // Matches each probe with a note that has the same line. |
| 513 | sameLine := func(x, y token.Pos) bool { |
| 514 | xp := prog.Fset.Position(x) |
| 515 | yp := prog.Fset.Position(y) |
| 516 | return xp.Filename == yp.Filename && xp.Line == yp.Line |
| 517 | } |
| 518 | expectations := make(map[*ssa.CallCommon]*expect.Note) |
| 519 | for call := range probes { |
| 520 | var match *expect.Note |
| 521 | for _, note := range notes { |
| 522 | if note.Name == "types" && sameLine(call.Pos(), note.Pos) { |
| 523 | match = note // first match is good enough. |
| 524 | break |
| 525 | } |
| 526 | } |
| 527 | if match != nil { |
| 528 | expectations[call] = match |
| 529 | } else { |
| 530 | t.Errorf("Unmatched probe: %v", call) |
| 531 | } |
| 532 | } |
| 533 | |
| 534 | // Check each expectation. |
| 535 | for call, note := range expectations { |
| 536 | var args []string |
| 537 | for _, a := range call.Args { |
| 538 | args = append(args, a.Type().String()) |
| 539 | } |
| 540 | if got, want := fmt.Sprint(args), fmt.Sprint(note.Args); got != want { |
| 541 | t.Errorf("Arguments to print() were expected to be %q. got %q", want, got) |
| 542 | } |
| 543 | } |
| 544 | }) |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | // TestInstructionString tests serializing instructions via Instruction.String(). |
| 549 | func TestInstructionString(t *testing.T) { |
| 550 | if !typeparams.Enabled { |
| 551 | t.Skip("TestInstructionString requires type parameters") |
| 552 | } |
| 553 | // Tests (ssa.Instruction).String(). Instructions are from a single go file. |
| 554 | // The Instructions tested are those that match a comment of the form: |
| 555 | // |
| 556 | // //@ instrs(f, kind, strs...) |
| 557 | // |
| 558 | // where f is the name of the function, kind is the type of the instructions matched |
| 559 | // within the function, and tests that the String() value for all of the instructions |
| 560 | // matched of String() is strs (in some order). |
| 561 | // See x/tools/go/expect for details on the syntax. |
| 562 | |
| 563 | const contents = ` |
| 564 | package p |
| 565 | |
| 566 | //@ instrs("f", "*ssa.TypeAssert") |
| 567 | //@ instrs("f", "*ssa.Call", "print(nil:interface{}, 0:int)") |
| 568 | func f(x int) { // non-generic smoke test. |
| 569 | var i interface{} |
| 570 | print(i, 0) |
| 571 | } |
| 572 | |
| 573 | //@ instrs("h", "*ssa.Alloc", "local T (u)") |
| 574 | //@ instrs("h", "*ssa.FieldAddr", "&t0.x [#0]") |
| 575 | func h[T ~struct{ x string }]() T { |
| 576 | u := T{"lorem"} |
| 577 | return u |
| 578 | } |
| 579 | |
| 580 | //@ instrs("c", "*ssa.TypeAssert", "typeassert t0.(interface{})") |
| 581 | //@ instrs("c", "*ssa.Call", "invoke x.foo()") |
| 582 | func c[T interface{ foo() string }](x T) { |
| 583 | _ = x.foo |
| 584 | _ = x.foo() |
| 585 | } |
| 586 | |
| 587 | //@ instrs("d", "*ssa.TypeAssert", "typeassert t0.(interface{})") |
| 588 | //@ instrs("d", "*ssa.Call", "invoke x.foo()") |
| 589 | func d[T interface{ foo() string; comparable }](x T) { |
| 590 | _ = x.foo |
| 591 | _ = x.foo() |
| 592 | } |
| 593 | ` |
| 594 | |
| 595 | // Parse |
| 596 | conf := loader.Config{ParserMode: parser.ParseComments} |
| 597 | const fname = "p.go" |
| 598 | f, err := conf.ParseFile(fname, contents) |
| 599 | if err != nil { |
| 600 | t.Fatalf("parse: %v", err) |
| 601 | } |
| 602 | conf.CreateFromFiles("p", f) |
| 603 | |
| 604 | // Load |
| 605 | lprog, err := conf.Load() |
| 606 | if err != nil { |
| 607 | t.Fatalf("Load: %v", err) |
| 608 | } |
| 609 | |
| 610 | // Create and build SSA |
| 611 | prog := ssa.NewProgram(lprog.Fset, ssa.SanityCheckFunctions) |
| 612 | for _, info := range lprog.AllPackages { |
| 613 | if info.TransitivelyErrorFree { |
| 614 | prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable) |
| 615 | } |
| 616 | } |
| 617 | p := prog.Package(lprog.Package("p").Pkg) |
| 618 | p.Build() |
| 619 | |
| 620 | // Collect all notes in f, i.e. comments starting with "//@ instr". |
| 621 | notes, err := expect.ExtractGo(prog.Fset, f) |
| 622 | if err != nil { |
| 623 | t.Errorf("expect.ExtractGo: %v", err) |
| 624 | } |
| 625 | |
| 626 | // Expectation is a {function, type string} -> {want, matches} |
| 627 | // where matches is all Instructions.String() that match the key. |
| 628 | // Each expecation is that some permutation of matches is wants. |
| 629 | type expKey struct { |
| 630 | function string |
| 631 | kind string |
| 632 | } |
| 633 | type expValue struct { |
| 634 | wants []string |
| 635 | matches []string |
| 636 | } |
| 637 | expectations := make(map[expKey]*expValue) |
| 638 | for _, note := range notes { |
| 639 | if note.Name == "instrs" { |
| 640 | if len(note.Args) < 2 { |
| 641 | t.Error("Had @instrs annotation without at least 2 arguments") |
| 642 | continue |
| 643 | } |
| 644 | fn, kind := fmt.Sprint(note.Args[0]), fmt.Sprint(note.Args[1]) |
| 645 | var wants []string |
| 646 | for _, arg := range note.Args[2:] { |
| 647 | wants = append(wants, fmt.Sprint(arg)) |
| 648 | } |
| 649 | expectations[expKey{fn, kind}] = &expValue{wants, nil} |
| 650 | } |
| 651 | } |
| 652 | |
| 653 | // Collect all Instructions that match the expectations. |
| 654 | for _, mem := range p.Members { |
| 655 | if fn, ok := mem.(*ssa.Function); ok { |
| 656 | for _, bb := range fn.Blocks { |
| 657 | for _, i := range bb.Instrs { |
| 658 | kind := fmt.Sprintf("%T", i) |
| 659 | if e := expectations[expKey{fn.Name(), kind}]; e != nil { |
| 660 | e.matches = append(e.matches, i.String()) |
| 661 | } |
| 662 | } |
| 663 | } |
| 664 | } |
| 665 | } |
| 666 | |
| 667 | // Check each expectation. |
| 668 | for key, value := range expectations { |
| 669 | if _, ok := p.Members[key.function]; !ok { |
| 670 | t.Errorf("Expectation on %s does not match a member in %s", key.function, p.Pkg.Name()) |
| 671 | } |
| 672 | got, want := value.matches, value.wants |
| 673 | sort.Strings(got) |
| 674 | sort.Strings(want) |
| 675 | if !reflect.DeepEqual(want, got) { |
| 676 | t.Errorf("Within %s wanted instructions of kind %s: %q. got %q", key.function, key.kind, want, got) |
| 677 | } |
| 678 | } |
| 679 | } |
| 680 |
Members