GoPLS Viewer

Home|gopls/go/ssa/builder_test.go
1// Copyright 2013 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
5package ssa_test
6
7import (
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/buildutil"
24    "golang.org/x/tools/go/loader"
25    "golang.org/x/tools/go/ssa"
26    "golang.org/x/tools/go/ssa/ssautil"
27    "golang.org/x/tools/internal/testenv"
28    "golang.org/x/tools/internal/typeparams"
29)
30
31func isEmpty(f *ssa.Functionbool { return f.Blocks == nil }
32
33// Tests that programs partially loaded from gc object files contain
34// functions with no code for the external portions, but are otherwise ok.
35func TestBuildPackage(t *testing.T) {
36    testenv.NeedsGoBuild(t// for importer.Default()
37
38    input := `
39package main
40
41import (
42    "bytes"
43    "io"
44    "testing"
45)
46
47func main() {
48        var t testing.T
49        t.Parallel()    // static call to external declared method
50        t.Fail()        // static call to promoted external declared method
51        testing.Short() // static call to external package-level function
52
53        var w io.Writer = new(bytes.Buffer)
54        w.Write(nil)    // interface invoke of external declared method
55}
56`
57
58    // Parse the file.
59    fset := token.NewFileSet()
60    ferr := parser.ParseFile(fset"input.go"input0)
61    if err != nil {
62        t.Error(err)
63        return
64    }
65
66    // Build an SSA program from the parsed file.
67    // Load its dependencies from gc binary export data.
68    mode := ssa.SanityCheckFunctions
69    mainPkg_err := ssautil.BuildPackage(&types.Config{Importerimporter.Default()}, fset,
70        types.NewPackage("main"""), []*ast.File{f}, mode)
71    if err != nil {
72        t.Error(err)
73        return
74    }
75
76    // The main package, its direct and indirect dependencies are loaded.
77    deps := []string{
78        // directly imported dependencies:
79        "bytes""io""testing",
80        // indirect dependencies mentioned by
81        // the direct imports' export data
82        "sync""unicode""time",
83    }
84
85    prog := mainPkg.Prog
86    all := prog.AllPackages()
87    if len(all) <= len(deps) {
88        t.Errorf("unexpected set of loaded packages: %q"all)
89    }
90    for _path := range deps {
91        pkg := prog.ImportedPackage(path)
92        if pkg == nil {
93            t.Errorf("package not loaded: %q"path)
94            continue
95        }
96
97        // External packages should have no function bodies (except for wrappers).
98        isExt := pkg != mainPkg
99
100        // init()
101        if isExt && !isEmpty(pkg.Func("init")) {
102            t.Errorf("external package %s has non-empty init"pkg)
103        } else if !isExt && isEmpty(pkg.Func("init")) {
104            t.Errorf("main package %s has empty init"pkg)
105        }
106
107        for _mem := range pkg.Members {
108            switch mem := mem.(type) {
109            case *ssa.Function:
110                // Functions at package level.
111                if isExt && !isEmpty(mem) {
112                    t.Errorf("external function %s is non-empty"mem)
113                } else if !isExt && isEmpty(mem) {
114                    t.Errorf("function %s is empty"mem)
115                }
116
117            case *ssa.Type:
118                // Methods of named types T.
119                // (In this test, all exported methods belong to *T not T.)
120                if !isExt {
121                    t.Fatalf("unexpected name type in main package: %s"mem)
122                }
123                mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
124                for in := 0mset.Len(); i < ni++ {
125                    m := prog.MethodValue(mset.At(i))
126                    // For external types, only synthetic wrappers have code.
127                    expExt := !strings.Contains(m.Synthetic"wrapper")
128                    if expExt && !isEmpty(m) {
129                        t.Errorf("external method %s is non-empty: %s",
130                            mm.Synthetic)
131                    } else if !expExt && isEmpty(m) {
132                        t.Errorf("method function %s is empty: %s",
133                            mm.Synthetic)
134                    }
135                }
136            }
137        }
138    }
139
140    expectedCallee := []string{
141        "(*testing.T).Parallel",
142        "(*testing.common).Fail",
143        "testing.Short",
144        "N/A",
145    }
146    callNum := 0
147    for _b := range mainPkg.Func("main").Blocks {
148        for _instr := range b.Instrs {
149            switch instr := instr.(type) {
150            case ssa.CallInstruction:
151                call := instr.Common()
152                if want := expectedCallee[callNum]; want != "N/A" {
153                    got := call.StaticCallee().String()
154                    if want != got {
155                        t.Errorf("call #%d from main.main: got callee %s, want %s",
156                            callNumgotwant)
157                    }
158                }
159                callNum++
160            }
161        }
162    }
163    if callNum != 4 {
164        t.Errorf("in main.main: got %d calls, want %d"callNum4)
165    }
166}
167
168// TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.
169func TestRuntimeTypes(t *testing.T) {
170    testenv.NeedsGoBuild(t// for importer.Default()
171
172    tests := []struct {
173        input string
174        want  []string
175    }{
176        // An exported package-level type is needed.
177        {`package A; type T struct{}; func (T) f() {}`,
178            []string{"*p.T""p.T"},
179        },
180        // An unexported package-level type is not needed.
181        {`package B; type t struct{}; func (t) f() {}`,
182            nil,
183        },
184        // Subcomponents of type of exported package-level var are needed.
185        {`package C; import "bytes"; var V struct {*bytes.Buffer}`,
186            []string{"*bytes.Buffer""*struct{*bytes.Buffer}""struct{*bytes.Buffer}"},
187        },
188        // Subcomponents of type of unexported package-level var are not needed.
189        {`package D; import "bytes"; var v struct {*bytes.Buffer}`,
190            nil,
191        },
192        // Subcomponents of type of exported package-level function are needed.
193        {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`,
194            []string{"*bytes.Buffer""struct{*bytes.Buffer}"},
195        },
196        // Subcomponents of type of unexported package-level function are not needed.
197        {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
198            nil,
199        },
200        // Subcomponents of type of exported method of uninstantiated unexported type are not needed.
201        {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`,
202            nil,
203        },
204        // ...unless used by MakeInterface.
205        {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
206            []string{"*bytes.Buffer""*p.x""p.x""struct{*bytes.Buffer}"},
207        },
208        // Subcomponents of type of unexported method are not needed.
209        {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,
210            []string{"*bytes.Buffer""*p.X""p.X""struct{*bytes.Buffer}"},
211        },
212        // Local types aren't needed.
213        {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
214            nil,
215        },
216        // ...unless used by MakeInterface.
217        {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
218            []string{"*bytes.Buffer""*p.T""p.T"},
219        },
220        // Types used as operand of MakeInterface are needed.
221        {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
222            []string{"*bytes.Buffer""struct{*bytes.Buffer}"},
223        },
224        // MakeInterface is optimized away when storing to a blank.
225        {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
226            nil,
227        },
228    }
229
230    if typeparams.Enabled {
231        tests = append(tests, []struct {
232            input string
233            want  []string
234        }{
235            // MakeInterface does not create runtime type for parameterized types.
236            {`package N; var g interface{}; func f[S any]() { var v []S; g = v }; `,
237                nil,
238            },
239        }...)
240    }
241    for _test := range tests {
242        // Parse the file.
243        fset := token.NewFileSet()
244        ferr := parser.ParseFile(fset"input.go"test.input0)
245        if err != nil {
246            t.Errorf("test %q: %s"test.input[:15], err)
247            continue
248        }
249
250        // Create a single-file main package.
251        // Load dependencies from gc binary export data.
252        mode := ssa.SanityCheckFunctions
253        ssapkg_err := ssautil.BuildPackage(&types.Config{Importerimporter.Default()}, fset,
254            types.NewPackage("p"""), []*ast.File{f}, mode)
255        if err != nil {
256            t.Errorf("test %q: %s"test.input[:15], err)
257            continue
258        }
259
260        var typstrs []string
261        for _T := range ssapkg.Prog.RuntimeTypes() {
262            typstrs = append(typstrsT.String())
263        }
264        sort.Strings(typstrs)
265
266        if !reflect.DeepEqual(typstrstest.want) {
267            t.Errorf("test 'package %s': got %q, want %q",
268                f.Name.Nametypstrstest.want)
269        }
270    }
271}
272
273// TestInit tests that synthesized init functions are correctly formed.
274// Bare init functions omit calls to dependent init functions and the use of
275// an init guard. They are useful in cases where the client uses a different
276// calling convention for init functions, or cases where it is easier for a
277// client to analyze bare init functions. Both of these aspects are used by
278// the llgo compiler for simpler integration with gccgo's runtime library,
279// and to simplify the analysis whereby it deduces which stores to globals
280// can be lowered to global initializers.
281func TestInit(t *testing.T) {
282    tests := []struct {
283        mode        ssa.BuilderMode
284        inputwant string
285    }{
286        {0`package A; import _ "errors"; var i int = 42`,
287            `# Name: A.init
288# Package: A
289# Synthetic: package initializer
290func init():
2910:                                                                entry P:0 S:2
292    t0 = *init$guard                                                   bool
293    if t0 goto 2 else 1
2941:                                                           init.start P:1 S:1
295    *init$guard = true:bool
296    t1 = errors.init()                                                   ()
297    *i = 42:int
298    jump 2
2992:                                                            init.done P:2 S:0
300    return
301
302`},
303        {ssa.BareInits`package B; import _ "errors"; var i int = 42`,
304            `# Name: B.init
305# Package: B
306# Synthetic: package initializer
307func init():
3080:                                                                entry P:0 S:0
309    *i = 42:int
310    return
311
312`},
313    }
314    for _test := range tests {
315        // Create a single-file main package.
316        var conf loader.Config
317        ferr := conf.ParseFile("<input>"test.input)
318        if err != nil {
319            t.Errorf("test %q: %s"test.input[:15], err)
320            continue
321        }
322        conf.CreateFromFiles(f.Name.Namef)
323
324        lprogerr := conf.Load()
325        if err != nil {
326            t.Errorf("test 'package %s': Load: %s"f.Name.Nameerr)
327            continue
328        }
329        prog := ssautil.CreateProgram(lprogtest.mode)
330        mainPkg := prog.Package(lprog.Created[0].Pkg)
331        prog.Build()
332        initFunc := mainPkg.Func("init")
333        if initFunc == nil {
334            t.Errorf("test 'package %s': no init function"f.Name.Name)
335            continue
336        }
337
338        var initbuf bytes.Buffer
339        _err = initFunc.WriteTo(&initbuf)
340        if err != nil {
341            t.Errorf("test 'package %s': WriteTo: %s"f.Name.Nameerr)
342            continue
343        }
344
345        if initbuf.String() != test.want {
346            t.Errorf("test 'package %s': got %s, want %s"f.Name.Nameinitbuf.String(), test.want)
347        }
348    }
349}
350
351// TestSyntheticFuncs checks that the expected synthetic functions are
352// created, reachable, and not duplicated.
353func TestSyntheticFuncs(t *testing.T) {
354    const input = `package P
355type T int
356func (T) f() int
357func (*T) g() int
358var (
359    // thunks
360    a = T.f
361    b = T.f
362    c = (struct{T}).f
363    d = (struct{T}).f
364    e = (*T).g
365    f = (*T).g
366    g = (struct{*T}).g
367    h = (struct{*T}).g
368
369    // bounds
370    i = T(0).f
371    j = T(0).f
372    k = new(T).g
373    l = new(T).g
374
375    // wrappers
376    m interface{} = struct{T}{}
377    n interface{} = struct{T}{}
378    o interface{} = struct{*T}{}
379    p interface{} = struct{*T}{}
380    q interface{} = new(struct{T})
381    r interface{} = new(struct{T})
382    s interface{} = new(struct{*T})
383    t interface{} = new(struct{*T})
384)
385`
386    // Parse
387    var conf loader.Config
388    ferr := conf.ParseFile("<input>"input)
389    if err != nil {
390        t.Fatalf("parse: %v"err)
391    }
392    conf.CreateFromFiles(f.Name.Namef)
393
394    // Load
395    lprogerr := conf.Load()
396    if err != nil {
397        t.Fatalf("Load: %v"err)
398    }
399
400    // Create and build SSA
401    prog := ssautil.CreateProgram(lprogssa.BuilderMode(0))
402    prog.Build()
403
404    // Enumerate reachable synthetic functions
405    want := map[string]string{
406        "(*P.T).g$bound""bound method wrapper for func (*P.T).g() int",
407        "(P.T).f$bound":  "bound method wrapper for func (P.T).f() int",
408
409        "(*P.T).g$thunk":         "thunk for func (*P.T).g() int",
410        "(P.T).f$thunk":          "thunk for func (P.T).f() int",
411        "(struct{*P.T}).g$thunk""thunk for func (*P.T).g() int",
412        "(struct{P.T}).f$thunk":  "thunk for func (P.T).f() int",
413
414        "(*P.T).f":          "wrapper for func (P.T).f() int",
415        "(*struct{*P.T}).f""wrapper for func (P.T).f() int",
416        "(*struct{*P.T}).g""wrapper for func (*P.T).g() int",
417        "(*struct{P.T}).f":  "wrapper for func (P.T).f() int",
418        "(*struct{P.T}).g":  "wrapper for func (*P.T).g() int",
419        "(struct{*P.T}).f":  "wrapper for func (P.T).f() int",
420        "(struct{*P.T}).g":  "wrapper for func (*P.T).g() int",
421        "(struct{P.T}).f":   "wrapper for func (P.T).f() int",
422
423        "P.init""package initializer",
424    }
425    for fn := range ssautil.AllFunctions(prog) {
426        if fn.Synthetic == "" {
427            continue
428        }
429        name := fn.String()
430        wantDescrok := want[name]
431        if !ok {
432            t.Errorf("got unexpected/duplicate func: %q: %q"namefn.Synthetic)
433            continue
434        }
435        delete(wantname)
436
437        if wantDescr != fn.Synthetic {
438            t.Errorf("(%s).Synthetic = %q, want %q"namefn.SyntheticwantDescr)
439        }
440    }
441    for fndescr := range want {
442        t.Errorf("want func: %q: %q"fndescr)
443    }
444}
445
446// TestPhiElimination ensures that dead phis, including those that
447// participate in a cycle, are properly eliminated.
448func TestPhiElimination(t *testing.T) {
449    const input = `
450package p
451
452func f() error
453
454func g(slice []int) {
455    for {
456        for range slice {
457            // e should not be lifted to a dead Ï†-node.
458            e := f()
459            h(e)
460        }
461    }
462}
463
464func h(error)
465`
466    // The SSA code for this function should look something like this:
467    // 0:
468    //         jump 1
469    // 1:
470    //         t0 = len(slice)
471    //         jump 2
472    // 2:
473    //         t1 = phi [1: -1:int, 3: t2]
474    //         t2 = t1 + 1:int
475    //         t3 = t2 < t0
476    //         if t3 goto 3 else 1
477    // 3:
478    //         t4 = f()
479    //         t5 = h(t4)
480    //         jump 2
481    //
482    // But earlier versions of the SSA construction algorithm would
483    // additionally generate this cycle of dead phis:
484    //
485    // 1:
486    //         t7 = phi [0: nil:error, 2: t8] #e
487    //         ...
488    // 2:
489    //         t8 = phi [1: t7, 3: t4] #e
490    //         ...
491
492    // Parse
493    var conf loader.Config
494    ferr := conf.ParseFile("<input>"input)
495    if err != nil {
496        t.Fatalf("parse: %v"err)
497    }
498    conf.CreateFromFiles("p"f)
499
500    // Load
501    lprogerr := conf.Load()
502    if err != nil {
503        t.Fatalf("Load: %v"err)
504    }
505
506    // Create and build SSA
507    prog := ssautil.CreateProgram(lprogssa.BuilderMode(0))
508    p := prog.Package(lprog.Package("p").Pkg)
509    p.Build()
510    g := p.Func("g")
511
512    phis := 0
513    for _b := range g.Blocks {
514        for _instr := range b.Instrs {
515            if _ok := instr.(*ssa.Phi); ok {
516                phis++
517            }
518        }
519    }
520    if phis != 1 {
521        g.WriteTo(os.Stderr)
522        t.Errorf("expected a single Phi (for the range index), got %d"phis)
523    }
524}
525
526// TestGenericDecls ensures that *unused* generic types, methods and functions
527// signatures can be built.
528//
529// TODO(taking): Add calls from non-generic functions to instantiations of generic functions.
530// TODO(taking): Add globals with types that are instantiations of generic functions.
531func TestGenericDecls(t *testing.T) {
532    if !typeparams.Enabled {
533        t.Skip("TestGenericDecls only works with type parameters enabled.")
534    }
535    const input = `
536package p
537
538import "unsafe"
539
540type Pointer[T any] struct {
541    v unsafe.Pointer
542}
543
544func (x *Pointer[T]) Load() *T {
545    return (*T)(LoadPointer(&x.v))
546}
547
548func Load[T any](x *Pointer[T]) *T {
549    return x.Load()
550}
551
552func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
553`
554    // The SSA members for this package should look something like this:
555    //          func  LoadPointer func(addr *unsafe.Pointer) (val unsafe.Pointer)
556    //      type  Pointer     struct{v unsafe.Pointer}
557    //        method (*Pointer[T any]) Load() *T
558    //      func  init        func()
559    //      var   init$guard  bool
560
561    // Parse
562    var conf loader.Config
563    ferr := conf.ParseFile("<input>"input)
564    if err != nil {
565        t.Fatalf("parse: %v"err)
566    }
567    conf.CreateFromFiles("p"f)
568
569    // Load
570    lprogerr := conf.Load()
571    if err != nil {
572        t.Fatalf("Load: %v"err)
573    }
574
575    // Create and build SSA
576    prog := ssautil.CreateProgram(lprogssa.BuilderMode(0))
577    p := prog.Package(lprog.Package("p").Pkg)
578    p.Build()
579
580    if load := p.Func("Load"); typeparams.ForSignature(load.Signature).Len() != 1 {
581        t.Errorf("expected a single type param T for Load got %q"load.Signature)
582    }
583    if ptr := p.Type("Pointer"); typeparams.ForNamed(ptr.Type().(*types.Named)).Len() != 1 {
584        t.Errorf("expected a single type param T for Pointer got %q"ptr.Type())
585    }
586}
587
588func TestGenericWrappers(t *testing.T) {
589    if !typeparams.Enabled {
590        t.Skip("TestGenericWrappers only works with type parameters enabled.")
591    }
592    const input = `
593package p
594
595type S[T any] struct {
596    t *T
597}
598
599func (x S[T]) M() T {
600    return *(x.t)
601}
602
603var thunk = S[int].M
604
605var g S[int]
606var bound = g.M
607
608type R[T any] struct{ S[T] }
609
610var indirect = R[int].M
611`
612    // The relevant SSA members for this package should look something like this:
613    // var   bound      func() int
614    // var   thunk      func(S[int]) int
615    // var   wrapper    func(R[int]) int
616
617    // Parse
618    var conf loader.Config
619    ferr := conf.ParseFile("<input>"input)
620    if err != nil {
621        t.Fatalf("parse: %v"err)
622    }
623    conf.CreateFromFiles("p"f)
624
625    // Load
626    lprogerr := conf.Load()
627    if err != nil {
628        t.Fatalf("Load: %v"err)
629    }
630
631    for _mode := range []ssa.BuilderMode{ssa.BuilderMode(0), ssa.InstantiateGenerics} {
632        // Create and build SSA
633        prog := ssautil.CreateProgram(lprogmode)
634        p := prog.Package(lprog.Package("p").Pkg)
635        p.Build()
636
637        for _entry := range []struct {
638            name    string // name of the package variable
639            typ     string // type of the package variable
640            wrapper string // wrapper function to which the package variable is set
641            callee  string // callee within the wrapper function
642        }{
643            {
644                "bound",
645                "*func() int",
646                "(p.S[int]).M$bound",
647                "(p.S[int]).M[int]",
648            },
649            {
650                "thunk",
651                "*func(p.S[int]) int",
652                "(p.S[int]).M$thunk",
653                "(p.S[int]).M[int]",
654            },
655            {
656                "indirect",
657                "*func(p.R[int]) int",
658                "(p.R[int]).M$thunk",
659                "(p.S[int]).M[int]",
660            },
661        } {
662            entry := entry
663            t.Run(entry.name, func(t *testing.T) {
664                v := p.Var(entry.name)
665                if v == nil {
666                    t.Fatalf("Did not find variable for %q in %s"entry.namep.String())
667                }
668                if v.Type().String() != entry.typ {
669                    t.Errorf("Expected type for variable %s: %q. got %q"ventry.typv.Type())
670                }
671
672                // Find the wrapper for v. This is stored exactly once in init.
673                var wrapper *ssa.Function
674                for _bb := range p.Func("init").Blocks {
675                    for _i := range bb.Instrs {
676                        if storeok := i.(*ssa.Store); ok && v == store.Addr {
677                            switch val := store.Val.(type) {
678                            case *ssa.Function:
679                                wrapper = val
680                            case *ssa.MakeClosure:
681                                wrapper = val.Fn.(*ssa.Function)
682                            }
683                        }
684                    }
685                }
686                if wrapper == nil {
687                    t.Fatalf("failed to find wrapper function for %s"entry.name)
688                }
689                if wrapper.String() != entry.wrapper {
690                    t.Errorf("Expected wrapper function %q. got %q"wrapperentry.wrapper)
691                }
692
693                // Find the callee within the wrapper. There should be exactly one call.
694                var callee *ssa.Function
695                for _bb := range wrapper.Blocks {
696                    for _i := range bb.Instrs {
697                        if callok := i.(*ssa.Call); ok {
698                            callee = call.Call.StaticCallee()
699                        }
700                    }
701                }
702                if callee == nil {
703                    t.Fatalf("failed to find callee within wrapper %s"wrapper)
704                }
705                if callee.String() != entry.callee {
706                    t.Errorf("Expected callee in wrapper %q is %q. got %q"ventry.calleecallee)
707                }
708            })
709        }
710    }
711}
712
713// TestTypeparamTest builds SSA over compilable examples in $GOROOT/test/typeparam/*.go.
714
715func TestTypeparamTest(t *testing.T) {
716    if !typeparams.Enabled {
717        return
718    }
719
720    // Tests use a fake goroot to stub out standard libraries with delcarations in
721    // testdata/src. Decreases runtime from ~80s to ~1s.
722
723    dir := filepath.Join(build.Default.GOROOT"test""typeparam")
724
725    // Collect all of the .go files in
726    listerr := os.ReadDir(dir)
727    if err != nil {
728        t.Fatal(err)
729    }
730
731    for _entry := range list {
732        if entry.Name() == "issue376214.go" {
733            continue // investigate variadic + New signature.
734        }
735        if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
736            continue // Consider standalone go files.
737        }
738        input := filepath.Join(direntry.Name())
739        t.Run(entry.Name(), func(t *testing.T) {
740            srcerr := os.ReadFile(input)
741            if err != nil {
742                t.Fatal(err)
743            }
744            // Only build test files that can be compiled, or compiled and run.
745            if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) {
746                t.Skipf("not detected as a run test")
747            }
748
749            t.Logf("Input: %s\n"input)
750
751            ctx := build.Default    // copy
752            ctx.GOROOT = "testdata" // fake goroot. Makes tests ~1s. tests take ~80s.
753
754            reportErr := func(err error) {
755                t.Error(err)
756            }
757            conf := loader.Config{Build: &ctxTypeCheckertypes.Config{ErrorreportErr}}
758            if _err := conf.FromArgs([]string{input}, true); err != nil {
759                t.Fatalf("FromArgs(%s) failed: %s"inputerr)
760            }
761
762            iprogerr := conf.Load()
763            if iprog != nil {
764                for _pkg := range iprog.Created {
765                    for ie := range pkg.Errors {
766                        t.Errorf("Loading pkg %s error[%d]=%s"pkgie)
767                    }
768                }
769            }
770            if err != nil {
771                t.Fatalf("conf.Load(%s) failed: %s"inputerr)
772            }
773
774            mode := ssa.SanityCheckFunctions | ssa.InstantiateGenerics
775            prog := ssautil.CreateProgram(iprogmode)
776            prog.Build()
777        })
778    }
779}
780
781// TestOrderOfOperations ensures order of operations are as intended.
782func TestOrderOfOperations(t *testing.T) {
783    // Testing for the order of operations within an expression is done
784    // by collecting the sequence of direct function calls within a *Function.
785    // Callees are all external functions so they cannot be safely re-ordered by ssa.
786    const input = `
787package p
788
789func a() int
790func b() int
791func c() int
792
793func slice(s []int) []int { return s[a():b()] }
794func sliceMax(s []int) []int { return s[a():b():c()] }
795
796`
797
798    // Parse
799    var conf loader.Config
800    ferr := conf.ParseFile("<input>"input)
801    if err != nil {
802        t.Fatalf("parse: %v"err)
803    }
804    conf.CreateFromFiles("p"f)
805
806    // Load
807    lprogerr := conf.Load()
808    if err != nil {
809        t.Fatalf("Load: %v"err)
810    }
811
812    // Create and build SSA
813    prog := ssautil.CreateProgram(lprogssa.BuilderMode(0))
814    p := prog.Package(lprog.Package("p").Pkg)
815    p.Build()
816
817    for _item := range []struct {
818        fn   string
819        want string // sequence of calls within the function.
820    }{
821        {"sliceMax""[a() b() c()]"},
822        {"slice""[a() b()]"},
823    } {
824        fn := p.Func(item.fn)
825        want := item.want
826        t.Run(item.fn, func(t *testing.T) {
827            t.Parallel()
828
829            var calls []string
830            for _b := range fn.Blocks {
831                for _instr := range b.Instrs {
832                    if callok := instr.(ssa.CallInstruction); ok {
833                        calls = append(callscall.String())
834                    }
835                }
836            }
837            if got := fmt.Sprint(calls); got != want {
838                fn.WriteTo(os.Stderr)
839                t.Errorf("Expected sequence of function calls in %s was %s. got %s"fnwantgot)
840            }
841        })
842    }
843}
844
845// TestGenericFunctionSelector ensures generic functions from other packages can be selected.
846func TestGenericFunctionSelector(t *testing.T) {
847    if !typeparams.Enabled {
848        t.Skip("TestGenericFunctionSelector uses type parameters.")
849    }
850
851    pkgs := map[string]map[string]string{
852        "main": {"m.go"`package main; import "a"; func main() { a.F[int](); a.G[int,string](); a.H(0) }`},
853        "a":    {"a.go"`package a; func F[T any](){}; func G[S, T any](){}; func H[T any](a T){} `},
854    }
855
856    for _mode := range []ssa.BuilderMode{
857        ssa.SanityCheckFunctions,
858        ssa.SanityCheckFunctions | ssa.InstantiateGenerics,
859    } {
860        conf := loader.Config{
861            Buildbuildutil.FakeContext(pkgs),
862        }
863        conf.Import("main")
864
865        lprogerr := conf.Load()
866        if err != nil {
867            t.Errorf("Load failed: %s"err)
868        }
869        if lprog == nil {
870            t.Fatalf("Load returned nil *Program")
871        }
872        // Create and build SSA
873        prog := ssautil.CreateProgram(lprogmode)
874        p := prog.Package(lprog.Package("main").Pkg)
875        p.Build()
876
877        var callees []string // callees of the CallInstruction.String() in main().
878        for _b := range p.Func("main").Blocks {
879            for _i := range b.Instrs {
880                if callok := i.(ssa.CallInstruction); ok {
881                    if callee := call.Common().StaticCallee(); call != nil {
882                        callees = append(calleescallee.String())
883                    } else {
884                        t.Errorf("CallInstruction without StaticCallee() %q"call)
885                    }
886                }
887            }
888        }
889        sort.Strings(callees// ignore the order in the code.
890
891        want := "[a.F[int] a.G[int string] a.H[int]]"
892        if got := fmt.Sprint(callees); got != want {
893            t.Errorf("Expected main() to contain calls %v. got %v"wantgot)
894        }
895    }
896}
897
MembersX
TestGenericWrappers.t
TestTypeparamTest.t
TestOrderOfOperations.prog
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.err
TestPhiElimination.prog
TestGenericDecls.conf
TestPhiElimination.RangeStmt_14275.b
TestGenericWrappers.RangeStmt_17037.BlockStmt.RangeStmt_17253.entry
TestTypeparamTest.RangeStmt_19804.BlockStmt.BlockStmt.prog
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.conf
TestBuildPackage.RangeStmt_2173.BlockStmt.RangeStmt_2642.BlockStmt.BlockStmt.i
TestRuntimeTypes.RangeStmt_7127.BlockStmt.f
TestPhiElimination.input
TestTypeparamTest.RangeStmt_19804.BlockStmt.BlockStmt.conf
TestSyntheticFuncs.RangeStmt_12456.BlockStmt.name
TestPhiElimination.t
TestGenericWrappers.err
TestGenericWrappers.RangeStmt_17037.BlockStmt.RangeStmt_17253.BlockStmt.BlockStmt.RangeStmt_18248.bb
TestOrderOfOperations.RangeStmt_22141.BlockStmt.want
TestBuildPackage.expectedCallee
TestRuntimeTypes.t
TestTypeparamTest.RangeStmt_19804.BlockStmt.BlockStmt.err
TestOrderOfOperations.RangeStmt_22141.BlockStmt.BlockStmt.got
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.p
TestPhiElimination
TestTypeparamTest.RangeStmt_19804.BlockStmt.input
TestInit.t
TestInit.RangeStmt_9519.BlockStmt.prog
TestInit.RangeStmt_9519.BlockStmt.initbuf
TestRuntimeTypes.RangeStmt_7127.BlockStmt.err
TestRuntimeTypes.RangeStmt_7127.BlockStmt.typstrs
TestSyntheticFuncs
TestRuntimeTypes.RangeStmt_7127.BlockStmt.fset
TestGenericDecls.ptr
TestSyntheticFuncs.f
TestOrderOfOperations.t
TestOrderOfOperations.err
TestGenericFunctionSelector.RangeStmt_23272.mode
TestBuildPackage.RangeStmt_2173.BlockStmt.RangeStmt_2642.BlockStmt.BlockStmt.mset
TestRuntimeTypes.tests
TestInit.RangeStmt_9519.BlockStmt.f
TestInit.RangeStmt_9519.BlockStmt.err
TestTypeparamTest.dir
TestBuildPackage
TestInit.RangeStmt_9519.test
TestBuildPackage.RangeStmt_3790.BlockStmt.RangeStmt_3840.BlockStmt.BlockStmt.BlockStmt.got
TestRuntimeTypes.RangeStmt_7127.BlockStmt.ssapkg
TestTypeparamTest.RangeStmt_19804.BlockStmt.BlockStmt._
TestTypeparamTest.RangeStmt_19804.BlockStmt.BlockStmt.BlockStmt.RangeStmt_20883.BlockStmt.RangeStmt_20924.e
TestBuildPackage.f
TestBuildPackage.mode
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.RangeStmt_23852.b
TestOrderOfOperations.RangeStmt_22141.BlockStmt.fn
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.lprog
TestTypeparamTest.RangeStmt_19804.entry
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.RangeStmt_23852.BlockStmt.RangeStmt_23897.BlockStmt.BlockStmt.callee
isEmpty
TestGenericDecls.t
TestInit
TestInit.RangeStmt_9519.BlockStmt.conf
TestGenericDecls.lprog
TestOrderOfOperations
TestOrderOfOperations.p
TestBuildPackage.err
TestBuildPackage.RangeStmt_3790.BlockStmt.RangeStmt_3840.instr
TestGenericDecls.f
TestGenericWrappers.input
TestGenericWrappers.conf
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.got
TestInit.tests
TestSyntheticFuncs.RangeStmt_12833.fn
TestOrderOfOperations.input
strings
TestBuildPackage.RangeStmt_2173.BlockStmt.RangeStmt_2642.BlockStmt.BlockStmt.n
TestGenericWrappers.lprog
TestGenericWrappers.RangeStmt_17037.BlockStmt.prog
TestOrderOfOperations.RangeStmt_22141.BlockStmt.BlockStmt.RangeStmt_22435.b
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.callees
TestBuildPackage.callNum
TestGenericDecls.err
TestGenericWrappers.RangeStmt_17037.BlockStmt.RangeStmt_17253.BlockStmt.BlockStmt.RangeStmt_18248.BlockStmt.RangeStmt_18296.i
TestGenericFunctionSelector
TestInit.RangeStmt_9519.BlockStmt.initFunc
TestGenericDecls
TestBuildPackage.RangeStmt_2173.BlockStmt.pkg
TestPhiElimination.RangeStmt_14275.BlockStmt.RangeStmt_14306.instr
TestGenericDecls.load
TestGenericWrappers
TestTypeparamTest.RangeStmt_19804.BlockStmt.BlockStmt.BlockStmt.RangeStmt_20883.BlockStmt.RangeStmt_20924.i
testenv
TestBuildPackage.mainPkg
TestSyntheticFuncs.prog
TestPhiElimination.err
TestGenericWrappers.RangeStmt_17037.BlockStmt.RangeStmt_17253.BlockStmt.BlockStmt.callee
TestBuildPackage.RangeStmt_2173.BlockStmt.RangeStmt_2642.BlockStmt.BlockStmt.BlockStmt.m
TestRuntimeTypes
TestSyntheticFuncs.want
TestPhiElimination.p
TestTypeparamTest.list
TestTypeparamTest.RangeStmt_19804.BlockStmt.BlockStmt.BlockStmt.RangeStmt_20883.pkg
TestOrderOfOperations.RangeStmt_22141.BlockStmt.BlockStmt.RangeStmt_22435.BlockStmt.RangeStmt_22469.instr
TestBuildPackage.deps
TestSyntheticFuncs.input
TestOrderOfOperations.RangeStmt_22141.item
TestInit.RangeStmt_9519.BlockStmt.mainPkg
TestPhiElimination.conf
TestGenericWrappers.RangeStmt_17037.BlockStmt.p
TestTypeparamTest.RangeStmt_19804.BlockStmt.BlockStmt.src
TestSyntheticFuncs.conf
TestGenericDecls.p
TestBuildPackage.fset
TestBuildPackage.RangeStmt_2173.BlockStmt.RangeStmt_2642.mem
TestSyntheticFuncs.lprog
TestPhiElimination.lprog
build
filepath
TestRuntimeTypes.RangeStmt_7127.test
TestRuntimeTypes.RangeStmt_7127.BlockStmt.RangeStmt_7731.T
TestTypeparamTest.RangeStmt_19804.BlockStmt.BlockStmt.iprog
TestOrderOfOperations.RangeStmt_22141.BlockStmt.BlockStmt.calls
bytes
TestBuildPackage.t
TestGenericWrappers.RangeStmt_17037.mode
TestGenericWrappers.RangeStmt_17037.BlockStmt.RangeStmt_17253.BlockStmt.BlockStmt.RangeStmt_18914.BlockStmt.RangeStmt_18955.i
TestTypeparamTest.err
TestGenericFunctionSelector.pkgs
TestBuildPackage.prog
TestSyntheticFuncs.err
TestBuildPackage.RangeStmt_3790.b
TestRuntimeTypes.RangeStmt_7127.BlockStmt.mode
TestPhiElimination.g
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.prog
TestBuildPackage.all
TestBuildPackage.RangeStmt_2173.path
TestGenericWrappers.RangeStmt_17037.BlockStmt.RangeStmt_17253.BlockStmt.BlockStmt.RangeStmt_18914.bb
TestTypeparamTest
TestOrderOfOperations.f
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.RangeStmt_23852.BlockStmt.RangeStmt_23897.i
TestBuildPackage.input
TestGenericWrappers.RangeStmt_17037.BlockStmt.RangeStmt_17253.BlockStmt.BlockStmt.v
TestBuildPackage._
TestGenericFunctionSelector.RangeStmt_23272.BlockStmt.want
TestTypeparamTest.RangeStmt_19804.BlockStmt.BlockStmt.ctx
TestOrderOfOperations.conf
TestSyntheticFuncs.RangeStmt_12456.fn
TestGenericDecls.prog
TestSyntheticFuncs.t
TestSyntheticFuncs.RangeStmt_12833.descr
TestPhiElimination.phis
TestGenericDecls.input
TestGenericWrappers.RangeStmt_17037.BlockStmt.RangeStmt_17253.BlockStmt.entry
TestGenericFunctionSelector.t
isEmpty.f
TestInit.RangeStmt_9519.BlockStmt.lprog
TestRuntimeTypes.RangeStmt_7127.BlockStmt._
TestPhiElimination.f
TestGenericWrappers.f
TestGenericWrappers.RangeStmt_17037.BlockStmt.RangeStmt_17253.BlockStmt.BlockStmt.wrapper
TestOrderOfOperations.lprog
buildutil
TestBuildPackage.RangeStmt_3790.BlockStmt.RangeStmt_3840.BlockStmt.BlockStmt.call
Members
X