1
2
3
4
5 package ssa_test
6
7 import (
8 "bytes"
9 "fmt"
10 "go/ast"
11 "go/build"
12 "go/importer"
13 "go/parser"
14 "go/token"
15 "go/types"
16 "os"
17 "path/filepath"
18 "reflect"
19 "sort"
20 "strings"
21 "testing"
22
23 "golang.org/x/tools/go/analysis/analysistest"
24 "golang.org/x/tools/go/buildutil"
25 "golang.org/x/tools/go/loader"
26 "golang.org/x/tools/go/packages"
27 "golang.org/x/tools/go/ssa"
28 "golang.org/x/tools/go/ssa/ssautil"
29 "golang.org/x/tools/internal/aliases"
30 "golang.org/x/tools/internal/testenv"
31 "golang.org/x/tools/internal/testfiles"
32 )
33
34 func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
35
36
37
38 func TestBuildPackage(t *testing.T) {
39 testenv.NeedsGoBuild(t)
40
41 input := `
42 package main
43
44 import (
45 "bytes"
46 "io"
47 "testing"
48 )
49
50 func main() {
51 var t testing.T
52 t.Parallel() // static call to external declared method
53 t.Fail() // static call to promoted external declared method
54 testing.Short() // static call to external package-level function
55
56 var w io.Writer = new(bytes.Buffer)
57 w.Write(nil) // interface invoke of external declared method
58 }
59 `
60
61
62 fset := token.NewFileSet()
63 f, err := parser.ParseFile(fset, "input.go", input, 0)
64 if err != nil {
65 t.Fatal(err)
66 return
67 }
68
69
70
71 mode := ssa.SanityCheckFunctions
72 mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
73 types.NewPackage("main", ""), []*ast.File{f}, mode)
74 if err != nil {
75 t.Fatal(err)
76 return
77 }
78
79
80 deps := []string{
81
82 "bytes", "io", "testing",
83
84
85 "sync", "unicode", "time",
86 }
87
88 prog := mainPkg.Prog
89 all := prog.AllPackages()
90 if len(all) <= len(deps) {
91 t.Errorf("unexpected set of loaded packages: %q", all)
92 }
93 for _, path := range deps {
94 pkg := prog.ImportedPackage(path)
95 if pkg == nil {
96 t.Errorf("package not loaded: %q", path)
97 continue
98 }
99
100
101 isExt := pkg != mainPkg
102
103
104 if isExt && !isEmpty(pkg.Func("init")) {
105 t.Errorf("external package %s has non-empty init", pkg)
106 } else if !isExt && isEmpty(pkg.Func("init")) {
107 t.Errorf("main package %s has empty init", pkg)
108 }
109
110 for _, mem := range pkg.Members {
111 switch mem := mem.(type) {
112 case *ssa.Function:
113
114 if isExt && !isEmpty(mem) {
115 t.Errorf("external function %s is non-empty", mem)
116 } else if !isExt && isEmpty(mem) {
117 t.Errorf("function %s is empty", mem)
118 }
119
120 case *ssa.Type:
121
122
123 if !isExt {
124 t.Fatalf("unexpected name type in main package: %s", mem)
125 }
126 mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
127 for i, n := 0, mset.Len(); i < n; i++ {
128 m := prog.MethodValue(mset.At(i))
129
130 expExt := !strings.Contains(m.Synthetic, "wrapper")
131 if expExt && !isEmpty(m) {
132 t.Errorf("external method %s is non-empty: %s",
133 m, m.Synthetic)
134 } else if !expExt && isEmpty(m) {
135 t.Errorf("method function %s is empty: %s",
136 m, m.Synthetic)
137 }
138 }
139 }
140 }
141 }
142
143 expectedCallee := []string{
144 "(*testing.T).Parallel",
145 "(*testing.common).Fail",
146 "testing.Short",
147 "N/A",
148 }
149 callNum := 0
150 for _, b := range mainPkg.Func("main").Blocks {
151 for _, instr := range b.Instrs {
152 switch instr := instr.(type) {
153 case ssa.CallInstruction:
154 call := instr.Common()
155 if want := expectedCallee[callNum]; want != "N/A" {
156 got := call.StaticCallee().String()
157 if want != got {
158 t.Errorf("call #%d from main.main: got callee %s, want %s",
159 callNum, got, want)
160 }
161 }
162 callNum++
163 }
164 }
165 }
166 if callNum != 4 {
167 t.Errorf("in main.main: got %d calls, want %d", callNum, 4)
168 }
169 }
170
171
172
173 func TestNoIndirectCreatePackage(t *testing.T) {
174 testenv.NeedsGoBuild(t)
175
176 dir := testfiles.ExtractTxtarToTmp(t, filepath.Join(analysistest.TestData(), "indirect.txtar"))
177 pkgs, err := loadPackages(dir, "testdata/a")
178 if err != nil {
179 t.Fatal(err)
180 }
181 a := pkgs[0]
182
183
184 prog := ssa.NewProgram(a.Fset, ssa.SanityCheckFunctions|ssa.PrintFunctions)
185 aSSA := prog.CreatePackage(a.Types, a.Syntax, a.TypesInfo, false)
186 for _, p := range a.Types.Imports() {
187 prog.CreatePackage(p, nil, nil, true)
188 }
189
190
191 aSSA.Build()
192
193
194 var got string
195 for _, instr := range aSSA.Members["A"].(*ssa.Function).Blocks[0].Instrs {
196 if call, ok := instr.(*ssa.Call); ok {
197 f := call.Call.Value.(*ssa.Function)
198 got = fmt.Sprintf("%v # %s", f, f.Synthetic)
199 break
200 }
201 }
202 want := "(testdata/c.C).F # from type information (on demand)"
203 if got != want {
204 t.Errorf("for sole call in a.A, got: <<%s>>, want <<%s>>", got, want)
205 }
206 }
207
208
209 func loadPackages(dir string, patterns ...string) ([]*packages.Package, error) {
210 cfg := &packages.Config{
211 Dir: dir,
212 Mode: packages.LoadSyntax,
213 Env: append(os.Environ(),
214 "GO111MODULES=on",
215 "GOPATH=",
216 "GOWORK=off",
217 "GOPROXY=off"),
218 }
219 pkgs, err := packages.Load(cfg, patterns...)
220 if err != nil {
221 return nil, err
222 }
223 if packages.PrintErrors(pkgs) > 0 {
224 return nil, fmt.Errorf("there were errors")
225 }
226 return pkgs, nil
227 }
228
229
230 func TestRuntimeTypes(t *testing.T) {
231 testenv.NeedsGoBuild(t)
232
233
234
235
236 tests := []struct {
237 input string
238 want []string
239 }{
240
241 {`package A; type T struct{}; func (T) f() {}; var x any = T{}`,
242 []string{"*p.T", "p.T"},
243 },
244
245 {`package B; type t struct{}; func (t) f() {}`,
246 nil,
247 },
248
249 {`package C; import "bytes"; var V struct {*bytes.Buffer}; var x any = &V`,
250 []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
251 },
252
253 {`package D; import "bytes"; var v struct {*bytes.Buffer}; var x any = v`,
254 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
255 },
256
257 {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}; var v any = F`,
258 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
259 },
260
261 {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}; var v any = f`,
262 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
263 },
264
265 {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`,
266 nil,
267 },
268
269 {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
270 []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"},
271 },
272
273 {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}; var x any = X{}`,
274 []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"},
275 },
276
277 {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
278 nil,
279 },
280
281 {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
282 []string{"*bytes.Buffer", "*p.T", "p.T"},
283 },
284
285 {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
286 []string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
287 },
288
289 {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
290 nil,
291 },
292
293 {`package N; var g interface{}; func f[S any]() { var v []S; g = v }; `,
294 nil,
295 },
296 }
297 for _, test := range tests {
298
299 fset := token.NewFileSet()
300 f, err := parser.ParseFile(fset, "input.go", test.input, 0)
301 if err != nil {
302 t.Errorf("test %q: %s", test.input[:15], err)
303 continue
304 }
305
306
307
308 mode := ssa.SanityCheckFunctions
309 ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
310 types.NewPackage("p", ""), []*ast.File{f}, mode)
311 if err != nil {
312 t.Errorf("test %q: %s", test.input[:15], err)
313 continue
314 }
315
316 var typstrs []string
317 for _, T := range ssapkg.Prog.RuntimeTypes() {
318 if types.IsInterface(T) || types.NewMethodSet(T).Len() == 0 {
319 continue
320 }
321 typstrs = append(typstrs, T.String())
322 }
323 sort.Strings(typstrs)
324
325 if !reflect.DeepEqual(typstrs, test.want) {
326 t.Errorf("test 'package %s': got %q, want %q",
327 f.Name.Name, typstrs, test.want)
328 }
329 }
330 }
331
332
333
334
335
336
337
338
339
340 func TestInit(t *testing.T) {
341 tests := []struct {
342 mode ssa.BuilderMode
343 input, want string
344 }{
345 {0, `package A; import _ "errors"; var i int = 42`,
346 `# Name: A.init
347 # Package: A
348 # Synthetic: package initializer
349 func init():
350 0: entry P:0 S:2
351 t0 = *init$guard bool
352 if t0 goto 2 else 1
353 1: init.start P:1 S:1
354 *init$guard = true:bool
355 t1 = errors.init() ()
356 *i = 42:int
357 jump 2
358 2: init.done P:2 S:0
359 return
360
361 `},
362 {ssa.BareInits, `package B; import _ "errors"; var i int = 42`,
363 `# Name: B.init
364 # Package: B
365 # Synthetic: package initializer
366 func init():
367 0: entry P:0 S:0
368 *i = 42:int
369 return
370
371 `},
372 }
373 for _, test := range tests {
374
375 var conf loader.Config
376 f, err := conf.ParseFile("<input>", test.input)
377 if err != nil {
378 t.Errorf("test %q: %s", test.input[:15], err)
379 continue
380 }
381 conf.CreateFromFiles(f.Name.Name, f)
382
383 lprog, err := conf.Load()
384 if err != nil {
385 t.Errorf("test 'package %s': Load: %s", f.Name.Name, err)
386 continue
387 }
388 prog := ssautil.CreateProgram(lprog, test.mode)
389 mainPkg := prog.Package(lprog.Created[0].Pkg)
390 prog.Build()
391 initFunc := mainPkg.Func("init")
392 if initFunc == nil {
393 t.Errorf("test 'package %s': no init function", f.Name.Name)
394 continue
395 }
396
397 var initbuf bytes.Buffer
398 _, err = initFunc.WriteTo(&initbuf)
399 if err != nil {
400 t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err)
401 continue
402 }
403
404 if initbuf.String() != test.want {
405 t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want)
406 }
407 }
408 }
409
410
411
412 func TestSyntheticFuncs(t *testing.T) {
413 const input = `package P
414 type T int
415 func (T) f() int
416 func (*T) g() int
417 var (
418 // thunks
419 a = T.f
420 b = T.f
421 c = (struct{T}).f
422 d = (struct{T}).f
423 e = (*T).g
424 f = (*T).g
425 g = (struct{*T}).g
426 h = (struct{*T}).g
427
428 // bounds
429 i = T(0).f
430 j = T(0).f
431 k = new(T).g
432 l = new(T).g
433
434 // wrappers
435 m interface{} = struct{T}{}
436 n interface{} = struct{T}{}
437 o interface{} = struct{*T}{}
438 p interface{} = struct{*T}{}
439 q interface{} = new(struct{T})
440 r interface{} = new(struct{T})
441 s interface{} = new(struct{*T})
442 t interface{} = new(struct{*T})
443 )
444 `
445
446 var conf loader.Config
447 f, err := conf.ParseFile("<input>", input)
448 if err != nil {
449 t.Fatalf("parse: %v", err)
450 }
451 conf.CreateFromFiles(f.Name.Name, f)
452
453
454 lprog, err := conf.Load()
455 if err != nil {
456 t.Fatalf("Load: %v", err)
457 }
458
459
460 prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0))
461 prog.Build()
462
463
464 want := map[string]string{
465 "(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int",
466 "(P.T).f$bound": "bound method wrapper for func (P.T).f() int",
467
468 "(*P.T).g$thunk": "thunk for func (*P.T).g() int",
469 "(P.T).f$thunk": "thunk for func (P.T).f() int",
470 "(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int",
471 "(struct{P.T}).f$thunk": "thunk for func (P.T).f() int",
472
473 "(*P.T).f": "wrapper for func (P.T).f() int",
474 "(*struct{*P.T}).f": "wrapper for func (P.T).f() int",
475 "(*struct{*P.T}).g": "wrapper for func (*P.T).g() int",
476 "(*struct{P.T}).f": "wrapper for func (P.T).f() int",
477 "(*struct{P.T}).g": "wrapper for func (*P.T).g() int",
478 "(struct{*P.T}).f": "wrapper for func (P.T).f() int",
479 "(struct{*P.T}).g": "wrapper for func (*P.T).g() int",
480 "(struct{P.T}).f": "wrapper for func (P.T).f() int",
481
482 "P.init": "package initializer",
483 }
484 var seen []string
485 for fn := range ssautil.AllFunctions(prog) {
486 if fn.Synthetic == "" {
487 continue
488 }
489 name := fn.String()
490 wantDescr, ok := want[name]
491 if !ok {
492 t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic)
493 continue
494 }
495 seen = append(seen, name)
496
497 if wantDescr != fn.Synthetic {
498 t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr)
499 }
500 }
501
502 for _, name := range seen {
503 delete(want, name)
504 }
505 for fn, descr := range want {
506 t.Errorf("want func: %q: %q", fn, descr)
507 }
508 }
509
510
511
512 func TestPhiElimination(t *testing.T) {
513 const input = `
514 package p
515
516 func f() error
517
518 func g(slice []int) {
519 for {
520 for range slice {
521 // e should not be lifted to a dead φ-node.
522 e := f()
523 h(e)
524 }
525 }
526 }
527
528 func h(error)
529 `
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557 var conf loader.Config
558 f, err := conf.ParseFile("<input>", input)
559 if err != nil {
560 t.Fatalf("parse: %v", err)
561 }
562 conf.CreateFromFiles("p", f)
563
564
565 lprog, err := conf.Load()
566 if err != nil {
567 t.Fatalf("Load: %v", err)
568 }
569
570
571 prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0))
572 p := prog.Package(lprog.Package("p").Pkg)
573 p.Build()
574 g := p.Func("g")
575
576 phis := 0
577 for _, b := range g.Blocks {
578 for _, instr := range b.Instrs {
579 if _, ok := instr.(*ssa.Phi); ok {
580 phis++
581 }
582 }
583 }
584 if phis != 1 {
585 g.WriteTo(os.Stderr)
586 t.Errorf("expected a single Phi (for the range index), got %d", phis)
587 }
588 }
589
590
591
592
593
594
595 func TestGenericDecls(t *testing.T) {
596 const input = `
597 package p
598
599 import "unsafe"
600
601 type Pointer[T any] struct {
602 v unsafe.Pointer
603 }
604
605 func (x *Pointer[T]) Load() *T {
606 return (*T)(LoadPointer(&x.v))
607 }
608
609 func Load[T any](x *Pointer[T]) *T {
610 return x.Load()
611 }
612
613 func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
614 `
615
616
617
618
619
620
621
622
623 var conf loader.Config
624 f, err := conf.ParseFile("<input>", input)
625 if err != nil {
626 t.Fatalf("parse: %v", err)
627 }
628 conf.CreateFromFiles("p", f)
629
630
631 lprog, err := conf.Load()
632 if err != nil {
633 t.Fatalf("Load: %v", err)
634 }
635
636
637 prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0))
638 p := prog.Package(lprog.Package("p").Pkg)
639 p.Build()
640
641 if load := p.Func("Load"); load.Signature.TypeParams().Len() != 1 {
642 t.Errorf("expected a single type param T for Load got %q", load.Signature)
643 }
644 if ptr := p.Type("Pointer"); ptr.Type().(*types.Named).TypeParams().Len() != 1 {
645 t.Errorf("expected a single type param T for Pointer got %q", ptr.Type())
646 }
647 }
648
649 func TestGenericWrappers(t *testing.T) {
650 const input = `
651 package p
652
653 type S[T any] struct {
654 t *T
655 }
656
657 func (x S[T]) M() T {
658 return *(x.t)
659 }
660
661 var thunk = S[int].M
662
663 var g S[int]
664 var bound = g.M
665
666 type R[T any] struct{ S[T] }
667
668 var indirect = R[int].M
669 `
670
671
672
673
674
675
676 var conf loader.Config
677 f, err := conf.ParseFile("<input>", input)
678 if err != nil {
679 t.Fatalf("parse: %v", err)
680 }
681 conf.CreateFromFiles("p", f)
682
683
684 lprog, err := conf.Load()
685 if err != nil {
686 t.Fatalf("Load: %v", err)
687 }
688
689 for _, mode := range []ssa.BuilderMode{ssa.BuilderMode(0), ssa.InstantiateGenerics} {
690
691 prog := ssautil.CreateProgram(lprog, mode)
692 p := prog.Package(lprog.Package("p").Pkg)
693 p.Build()
694
695 for _, entry := range []struct {
696 name string
697 typ string
698 wrapper string
699 callee string
700 }{
701 {
702 "bound",
703 "*func() int",
704 "(p.S[int]).M$bound",
705 "(p.S[int]).M[int]",
706 },
707 {
708 "thunk",
709 "*func(p.S[int]) int",
710 "(p.S[int]).M$thunk",
711 "(p.S[int]).M[int]",
712 },
713 {
714 "indirect",
715 "*func(p.R[int]) int",
716 "(p.R[int]).M$thunk",
717 "(p.S[int]).M[int]",
718 },
719 } {
720 entry := entry
721 t.Run(entry.name, func(t *testing.T) {
722 v := p.Var(entry.name)
723 if v == nil {
724 t.Fatalf("Did not find variable for %q in %s", entry.name, p.String())
725 }
726 if v.Type().String() != entry.typ {
727 t.Errorf("Expected type for variable %s: %q. got %q", v, entry.typ, v.Type())
728 }
729
730
731 var wrapper *ssa.Function
732 for _, bb := range p.Func("init").Blocks {
733 for _, i := range bb.Instrs {
734 if store, ok := i.(*ssa.Store); ok && v == store.Addr {
735 switch val := store.Val.(type) {
736 case *ssa.Function:
737 wrapper = val
738 case *ssa.MakeClosure:
739 wrapper = val.Fn.(*ssa.Function)
740 }
741 }
742 }
743 }
744 if wrapper == nil {
745 t.Fatalf("failed to find wrapper function for %s", entry.name)
746 }
747 if wrapper.String() != entry.wrapper {
748 t.Errorf("Expected wrapper function %q. got %q", wrapper, entry.wrapper)
749 }
750
751
752 var callee *ssa.Function
753 for _, bb := range wrapper.Blocks {
754 for _, i := range bb.Instrs {
755 if call, ok := i.(*ssa.Call); ok {
756 callee = call.Call.StaticCallee()
757 }
758 }
759 }
760 if callee == nil {
761 t.Fatalf("failed to find callee within wrapper %s", wrapper)
762 }
763 if callee.String() != entry.callee {
764 t.Errorf("Expected callee in wrapper %q is %q. got %q", v, entry.callee, callee)
765 }
766 })
767 }
768 }
769 }
770
771
772
773 func TestTypeparamTest(t *testing.T) {
774
775
776
777 dir := filepath.Join(build.Default.GOROOT, "test", "typeparam")
778
779
780 list, err := os.ReadDir(dir)
781 if err != nil {
782 t.Fatal(err)
783 }
784
785 for _, entry := range list {
786 if entry.Name() == "issue58513.go" {
787 continue
788 }
789 if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
790 continue
791 }
792 input := filepath.Join(dir, entry.Name())
793 t.Run(entry.Name(), func(t *testing.T) {
794 src, err := os.ReadFile(input)
795 if err != nil {
796 t.Fatal(err)
797 }
798
799 if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) {
800 t.Skipf("not detected as a run test")
801 }
802
803 t.Logf("Input: %s\n", input)
804
805 ctx := build.Default
806 ctx.GOROOT = "testdata"
807
808 reportErr := func(err error) {
809 t.Error(err)
810 }
811 conf := loader.Config{Build: &ctx, TypeChecker: types.Config{Error: reportErr}}
812 if _, err := conf.FromArgs([]string{input}, true); err != nil {
813 t.Fatalf("FromArgs(%s) failed: %s", input, err)
814 }
815
816 iprog, err := conf.Load()
817 if iprog != nil {
818 for _, pkg := range iprog.Created {
819 for i, e := range pkg.Errors {
820 t.Errorf("Loading pkg %s error[%d]=%s", pkg, i, e)
821 }
822 }
823 }
824 if err != nil {
825 t.Fatalf("conf.Load(%s) failed: %s", input, err)
826 }
827
828 mode := ssa.SanityCheckFunctions | ssa.InstantiateGenerics
829 prog := ssautil.CreateProgram(iprog, mode)
830 prog.Build()
831 })
832 }
833 }
834
835
836 func TestOrderOfOperations(t *testing.T) {
837
838
839
840 const input = `
841 package p
842
843 func a() int
844 func b() int
845 func c() int
846
847 func slice(s []int) []int { return s[a():b()] }
848 func sliceMax(s []int) []int { return s[a():b():c()] }
849
850 `
851
852
853 var conf loader.Config
854 f, err := conf.ParseFile("<input>", input)
855 if err != nil {
856 t.Fatalf("parse: %v", err)
857 }
858 conf.CreateFromFiles("p", f)
859
860
861 lprog, err := conf.Load()
862 if err != nil {
863 t.Fatalf("Load: %v", err)
864 }
865
866
867 prog := ssautil.CreateProgram(lprog, ssa.BuilderMode(0))
868 p := prog.Package(lprog.Package("p").Pkg)
869 p.Build()
870
871 for _, item := range []struct {
872 fn string
873 want string
874 }{
875 {"sliceMax", "[a() b() c()]"},
876 {"slice", "[a() b()]"},
877 } {
878 fn := p.Func(item.fn)
879 want := item.want
880 t.Run(item.fn, func(t *testing.T) {
881 t.Parallel()
882
883 var calls []string
884 for _, b := range fn.Blocks {
885 for _, instr := range b.Instrs {
886 if call, ok := instr.(ssa.CallInstruction); ok {
887 calls = append(calls, call.String())
888 }
889 }
890 }
891 if got := fmt.Sprint(calls); got != want {
892 fn.WriteTo(os.Stderr)
893 t.Errorf("Expected sequence of function calls in %s was %s. got %s", fn, want, got)
894 }
895 })
896 }
897 }
898
899
900 func TestGenericFunctionSelector(t *testing.T) {
901 pkgs := map[string]map[string]string{
902 "main": {"m.go": `package main; import "a"; func main() { a.F[int](); a.G[int,string](); a.H(0) }`},
903 "a": {"a.go": `package a; func F[T any](){}; func G[S, T any](){}; func H[T any](a T){} `},
904 }
905
906 for _, mode := range []ssa.BuilderMode{
907 ssa.SanityCheckFunctions,
908 ssa.SanityCheckFunctions | ssa.InstantiateGenerics,
909 } {
910 conf := loader.Config{
911 Build: buildutil.FakeContext(pkgs),
912 }
913 conf.Import("main")
914
915 lprog, err := conf.Load()
916 if err != nil {
917 t.Errorf("Load failed: %s", err)
918 }
919 if lprog == nil {
920 t.Fatalf("Load returned nil *Program")
921 }
922
923 prog := ssautil.CreateProgram(lprog, mode)
924 p := prog.Package(lprog.Package("main").Pkg)
925 p.Build()
926
927 var callees []string
928 for _, b := range p.Func("main").Blocks {
929 for _, i := range b.Instrs {
930 if call, ok := i.(ssa.CallInstruction); ok {
931 if callee := call.Common().StaticCallee(); call != nil {
932 callees = append(callees, callee.String())
933 } else {
934 t.Errorf("CallInstruction without StaticCallee() %q", call)
935 }
936 }
937 }
938 }
939 sort.Strings(callees)
940
941 want := "[a.F[int] a.G[int string] a.H[int]]"
942 if got := fmt.Sprint(callees); got != want {
943 t.Errorf("Expected main() to contain calls %v. got %v", want, got)
944 }
945 }
946 }
947
948 func TestIssue58491(t *testing.T) {
949
950 src := `
951 package p
952
953 func foo[T any](blocking func() (T, error)) error {
954 type result struct {
955 res T
956 error // ensure the method set of result is non-empty
957 }
958
959 res := make(chan result, 1)
960 go func() {
961 var r result
962 r.res, r.error = blocking()
963 res <- r
964 }()
965 r := <-res
966 err := r // require the rtype for result when instantiated
967 return err
968 }
969 var Inst = foo[int]
970 `
971 fset := token.NewFileSet()
972 f, err := parser.ParseFile(fset, "p.go", src, 0)
973 if err != nil {
974 t.Fatal(err)
975 }
976 files := []*ast.File{f}
977
978 pkg := types.NewPackage("p", "")
979 conf := &types.Config{}
980 p, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions|ssa.InstantiateGenerics)
981 if err != nil {
982 t.Fatalf("unexpected error: %v", err)
983 }
984
985
986 var found bool
987 for _, rt := range p.Prog.RuntimeTypes() {
988 if n, ok := rt.(*types.Named); ok {
989 if u, ok := n.Underlying().(*types.Struct); ok {
990 found = true
991 if got, want := n.String(), "p.result"; got != want {
992 t.Errorf("Expected the name %s got: %s", want, got)
993 }
994 if got, want := u.String(), "struct{res int; error}"; got != want {
995 t.Errorf("Expected the underlying type of %s to be %s. got %s", n, want, got)
996 }
997 }
998 }
999 }
1000 if !found {
1001 t.Error("Failed to find any Named to struct types")
1002 }
1003 }
1004
1005 func TestIssue58491Rec(t *testing.T) {
1006
1007 src := `
1008 package p
1009
1010 func foo[T any]() error {
1011 type result struct {
1012 res T
1013 next *result
1014 error // ensure the method set of result is non-empty
1015 }
1016
1017 r := &result{}
1018 err := r // require the rtype for result when instantiated
1019 return err
1020 }
1021 var Inst = foo[int]
1022 `
1023 fset := token.NewFileSet()
1024 f, err := parser.ParseFile(fset, "p.go", src, 0)
1025 if err != nil {
1026 t.Fatal(err)
1027 }
1028 files := []*ast.File{f}
1029
1030 pkg := types.NewPackage("p", "")
1031 conf := &types.Config{}
1032 p, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions|ssa.InstantiateGenerics)
1033 if err != nil {
1034 t.Fatalf("unexpected error: %v", err)
1035 }
1036
1037
1038 var found bool
1039 for _, rt := range p.Prog.RuntimeTypes() {
1040 if n, ok := aliases.Unalias(rt).(*types.Named); ok {
1041 if u, ok := n.Underlying().(*types.Struct); ok {
1042 found = true
1043 if got, want := n.String(), "p.result"; got != want {
1044 t.Errorf("Expected the name %s got: %s", want, got)
1045 }
1046 if got, want := u.String(), "struct{res int; next *p.result; error}"; got != want {
1047 t.Errorf("Expected the underlying type of %s to be %s. got %s", n, want, got)
1048 }
1049 }
1050 }
1051 }
1052 if !found {
1053 t.Error("Failed to find any Named to struct types")
1054 }
1055 }
1056
1057
1058 func TestSyntax(t *testing.T) {
1059 const input = `package p
1060
1061 type P int
1062 func (x *P) g() *P { return x }
1063
1064 func F[T ~int]() *T {
1065 type S1 *T
1066 type S2 *T
1067 type S3 *T
1068 f1 := func() S1 {
1069 f2 := func() S2 {
1070 return S2(nil)
1071 }
1072 return S1(f2())
1073 }
1074 f3 := func() S3 {
1075 return S3(f1())
1076 }
1077 return (*T)(f3())
1078 }
1079 var g = F[int]
1080 var _ = F[P] // unreferenced => not instantiated
1081 `
1082
1083
1084 var conf loader.Config
1085 f, err := conf.ParseFile("<input>", input)
1086 if err != nil {
1087 t.Fatalf("parse: %v", err)
1088 }
1089 conf.CreateFromFiles("p", f)
1090
1091
1092 lprog, err := conf.Load()
1093 if err != nil {
1094 t.Fatalf("Load: %v", err)
1095 }
1096
1097
1098 prog := ssautil.CreateProgram(lprog, ssa.InstantiateGenerics)
1099 prog.Build()
1100
1101
1102 got := make(map[string]string)
1103 for fn := range ssautil.AllFunctions(prog) {
1104 if fn.Name() == "init" {
1105 continue
1106 }
1107 syntax := fn.Syntax()
1108 if got[fn.Name()] != "" {
1109 t.Error("dup")
1110 }
1111 got[fn.Name()] = fmt.Sprintf("%T : %s @ %d", syntax, fn.Signature, prog.Fset.Position(syntax.Pos()).Line)
1112 }
1113
1114 want := map[string]string{
1115 "g": "*ast.FuncDecl : func() *p.P @ 4",
1116 "F": "*ast.FuncDecl : func[T ~int]() *T @ 6",
1117 "F$1": "*ast.FuncLit : func() p.S1 @ 10",
1118 "F$1$1": "*ast.FuncLit : func() p.S2 @ 11",
1119 "F$2": "*ast.FuncLit : func() p.S3 @ 16",
1120 "F[int]": "*ast.FuncDecl : func() *int @ 6",
1121 "F[int]$1": "*ast.FuncLit : func() p.S1 @ 10",
1122 "F[int]$1$1": "*ast.FuncLit : func() p.S2 @ 11",
1123 "F[int]$2": "*ast.FuncLit : func() p.S3 @ 16",
1124
1125
1126 }
1127 if !reflect.DeepEqual(got, want) {
1128 t.Errorf("Expected the functions with signature to be:\n\t%#v.\n Got:\n\t%#v", want, got)
1129 }
1130 }
1131
1132 func TestGo117Builtins(t *testing.T) {
1133 tests := []struct {
1134 name string
1135 src string
1136 importer types.Importer
1137 }{
1138 {"slice to array pointer", "package p; var s []byte; var _ = (*[4]byte)(s)", nil},
1139 {"unsafe slice", `package p; import "unsafe"; var _ = unsafe.Add(nil, 0)`, importer.Default()},
1140 {"unsafe add", `package p; import "unsafe"; var _ = unsafe.Slice((*int)(nil), 0)`, importer.Default()},
1141 }
1142
1143 for _, tc := range tests {
1144 tc := tc
1145 t.Run(tc.name, func(t *testing.T) {
1146 t.Parallel()
1147 fset := token.NewFileSet()
1148 f, err := parser.ParseFile(fset, "p.go", tc.src, parser.ParseComments)
1149 if err != nil {
1150 t.Error(err)
1151 }
1152 files := []*ast.File{f}
1153
1154 pkg := types.NewPackage("p", "")
1155 conf := &types.Config{Importer: tc.importer}
1156 if _, _, err := ssautil.BuildPackage(conf, fset, pkg, files, ssa.SanityCheckFunctions); err != nil {
1157 t.Error(err)
1158 }
1159 })
1160 }
1161 }
1162
1163
1164 func TestLabels(t *testing.T) {
1165 tests := []string{
1166 `package main
1167 func main() { _:println(1) }`,
1168 `package main
1169 func main() { _:println(1); _:println(2)}`,
1170 }
1171 for _, test := range tests {
1172 conf := loader.Config{Fset: token.NewFileSet()}
1173 f, err := parser.ParseFile(conf.Fset, "<input>", test, 0)
1174 if err != nil {
1175 t.Errorf("parse error: %s", err)
1176 return
1177 }
1178 conf.CreateFromFiles("main", f)
1179 iprog, err := conf.Load()
1180 if err != nil {
1181 t.Error(err)
1182 continue
1183 }
1184 prog := ssautil.CreateProgram(iprog, ssa.BuilderMode(0))
1185 pkg := prog.Package(iprog.Created[0].Pkg)
1186 pkg.Build()
1187 }
1188 }
1189
1190
1191
1192
1193 func TestRangeOverFuncNoPanic(t *testing.T) {
1194 testenv.NeedsGoBuild(t)
1195 testenv.NeedsGo1Point(t, 23)
1196
1197 dir := t.TempDir()
1198 if err := os.WriteFile(filepath.Join(dir, "rangeoverfunc.go"), []byte(`
1199 package main
1200
1201 func main() {
1202 for x := range ten {
1203 println(x)
1204 }
1205 }
1206
1207 func ten(yield func(int) bool) {
1208 for i := range 10 {
1209 if !yield(i) {
1210 return
1211 }
1212 }
1213 }
1214 `), 0666); err != nil {
1215 t.Fatal(err)
1216 }
1217 pkgs, err := loadPackages(dir, "./rangeoverfunc.go")
1218 if err != nil {
1219 t.Fatal(err)
1220 }
1221 pkg := pkgs[0]
1222
1223 const mode = ssa.SanityCheckFunctions
1224 prog := ssa.NewProgram(pkg.Fset, mode)
1225 ssapkg := prog.CreatePackage(pkg.Types, pkg.Syntax, pkg.TypesInfo, false)
1226 ssapkg.Build()
1227
1228
1229
1230
1231
1232
1233
1234 main := ssapkg.Members["main"].(*ssa.Function)
1235 firstBlock := main.Blocks[0]
1236 lastInstr := firstBlock.Instrs[len(firstBlock.Instrs)-1]
1237 if _, ok := lastInstr.(*ssa.Panic); !ok {
1238 t.Errorf("expected range-over-func to compile to panic, got %T", lastInstr)
1239 }
1240 }
1241
View as plain text