X-Git-Url: https://git.josue.xyz/?a=blobdiff_plain;f=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%40v0.0.0-20201105173854-bc9fc8d8c4bc%2Fgo%2Fssa%2Fbuilder_test.go;fp=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%40v0.0.0-20201105173854-bc9fc8d8c4bc%2Fgo%2Fssa%2Fbuilder_test.go;h=c45f930b3aa7e75414107f0859a876d742650a3f;hb=4d07c77cf4d78cab8639e13ddc3c22495e585b0b;hp=0000000000000000000000000000000000000000;hpb=b3950616b54221c40a7dab9099bda675007e5b6e;p=dotfiles%2F.git diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/go/ssa/builder_test.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/go/ssa/builder_test.go new file mode 100644 index 00000000..c45f930b --- /dev/null +++ b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/go/ssa/builder_test.go @@ -0,0 +1,500 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssa_test + +import ( + "bytes" + "go/ast" + "go/importer" + "go/parser" + "go/token" + "go/types" + "os" + "reflect" + "sort" + "strings" + "testing" + + "golang.org/x/tools/go/loader" + "golang.org/x/tools/go/ssa" + "golang.org/x/tools/go/ssa/ssautil" +) + +func isEmpty(f *ssa.Function) bool { return f.Blocks == nil } + +// Tests that programs partially loaded from gc object files contain +// functions with no code for the external portions, but are otherwise ok. +func TestBuildPackage(t *testing.T) { + input := ` +package main + +import ( + "bytes" + "io" + "testing" +) + +func main() { + var t testing.T + t.Parallel() // static call to external declared method + t.Fail() // static call to promoted external declared method + testing.Short() // static call to external package-level function + + var w io.Writer = new(bytes.Buffer) + w.Write(nil) // interface invoke of external declared method +} +` + + // Parse the file. + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "input.go", input, 0) + if err != nil { + t.Error(err) + return + } + + // Build an SSA program from the parsed file. + // Load its dependencies from gc binary export data. + mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, + types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions) + if err != nil { + t.Error(err) + return + } + + // The main package, its direct and indirect dependencies are loaded. + deps := []string{ + // directly imported dependencies: + "bytes", "io", "testing", + // indirect dependencies mentioned by + // the direct imports' export data + "sync", "unicode", "time", + } + + prog := mainPkg.Prog + all := prog.AllPackages() + if len(all) <= len(deps) { + t.Errorf("unexpected set of loaded packages: %q", all) + } + for _, path := range deps { + pkg := prog.ImportedPackage(path) + if pkg == nil { + t.Errorf("package not loaded: %q", path) + continue + } + + // External packages should have no function bodies (except for wrappers). + isExt := pkg != mainPkg + + // init() + if isExt && !isEmpty(pkg.Func("init")) { + t.Errorf("external package %s has non-empty init", pkg) + } else if !isExt && isEmpty(pkg.Func("init")) { + t.Errorf("main package %s has empty init", pkg) + } + + for _, mem := range pkg.Members { + switch mem := mem.(type) { + case *ssa.Function: + // Functions at package level. + if isExt && !isEmpty(mem) { + t.Errorf("external function %s is non-empty", mem) + } else if !isExt && isEmpty(mem) { + t.Errorf("function %s is empty", mem) + } + + case *ssa.Type: + // Methods of named types T. + // (In this test, all exported methods belong to *T not T.) + if !isExt { + t.Fatalf("unexpected name type in main package: %s", mem) + } + mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type())) + for i, n := 0, mset.Len(); i < n; i++ { + m := prog.MethodValue(mset.At(i)) + // For external types, only synthetic wrappers have code. + expExt := !strings.Contains(m.Synthetic, "wrapper") + if expExt && !isEmpty(m) { + t.Errorf("external method %s is non-empty: %s", + m, m.Synthetic) + } else if !expExt && isEmpty(m) { + t.Errorf("method function %s is empty: %s", + m, m.Synthetic) + } + } + } + } + } + + expectedCallee := []string{ + "(*testing.T).Parallel", + "(*testing.common).Fail", + "testing.Short", + "N/A", + } + callNum := 0 + for _, b := range mainPkg.Func("main").Blocks { + for _, instr := range b.Instrs { + switch instr := instr.(type) { + case ssa.CallInstruction: + call := instr.Common() + if want := expectedCallee[callNum]; want != "N/A" { + got := call.StaticCallee().String() + if want != got { + t.Errorf("call #%d from main.main: got callee %s, want %s", + callNum, got, want) + } + } + callNum++ + } + } + } + if callNum != 4 { + t.Errorf("in main.main: got %d calls, want %d", callNum, 4) + } +} + +// TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types. +func TestRuntimeTypes(t *testing.T) { + tests := []struct { + input string + want []string + }{ + // An exported package-level type is needed. + {`package A; type T struct{}; func (T) f() {}`, + []string{"*p.T", "p.T"}, + }, + // An unexported package-level type is not needed. + {`package B; type t struct{}; func (t) f() {}`, + nil, + }, + // Subcomponents of type of exported package-level var are needed. + {`package C; import "bytes"; var V struct {*bytes.Buffer}`, + []string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"}, + }, + // Subcomponents of type of unexported package-level var are not needed. + {`package D; import "bytes"; var v struct {*bytes.Buffer}`, + nil, + }, + // Subcomponents of type of exported package-level function are needed. + {`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`, + []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, + }, + // Subcomponents of type of unexported package-level function are not needed. + {`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`, + nil, + }, + // Subcomponents of type of exported method of uninstantiated unexported type are not needed. + {`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`, + nil, + }, + // ...unless used by MakeInterface. + {`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`, + []string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"}, + }, + // Subcomponents of type of unexported method are not needed. + {`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`, + []string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"}, + }, + // Local types aren't needed. + {`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`, + nil, + }, + // ...unless used by MakeInterface. + {`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`, + []string{"*bytes.Buffer", "*p.T", "p.T"}, + }, + // Types used as operand of MakeInterface are needed. + {`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`, + []string{"*bytes.Buffer", "struct{*bytes.Buffer}"}, + }, + // MakeInterface is optimized away when storing to a blank. + {`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`, + nil, + }, + } + for _, test := range tests { + // Parse the file. + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "input.go", test.input, 0) + if err != nil { + t.Errorf("test %q: %s", test.input[:15], err) + continue + } + + // Create a single-file main package. + // Load dependencies from gc binary export data. + ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, + types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions) + if err != nil { + t.Errorf("test %q: %s", test.input[:15], err) + continue + } + + var typstrs []string + for _, T := range ssapkg.Prog.RuntimeTypes() { + typstrs = append(typstrs, T.String()) + } + sort.Strings(typstrs) + + if !reflect.DeepEqual(typstrs, test.want) { + t.Errorf("test 'package %s': got %q, want %q", + f.Name.Name, typstrs, test.want) + } + } +} + +// TestInit tests that synthesized init functions are correctly formed. +// Bare init functions omit calls to dependent init functions and the use of +// an init guard. They are useful in cases where the client uses a different +// calling convention for init functions, or cases where it is easier for a +// client to analyze bare init functions. Both of these aspects are used by +// the llgo compiler for simpler integration with gccgo's runtime library, +// and to simplify the analysis whereby it deduces which stores to globals +// can be lowered to global initializers. +func TestInit(t *testing.T) { + tests := []struct { + mode ssa.BuilderMode + input, want string + }{ + {0, `package A; import _ "errors"; var i int = 42`, + `# Name: A.init +# Package: A +# Synthetic: package initializer +func init(): +0: entry P:0 S:2 + t0 = *init$guard bool + if t0 goto 2 else 1 +1: init.start P:1 S:1 + *init$guard = true:bool + t1 = errors.init() () + *i = 42:int + jump 2 +2: init.done P:2 S:0 + return + +`}, + {ssa.BareInits, `package B; import _ "errors"; var i int = 42`, + `# Name: B.init +# Package: B +# Synthetic: package initializer +func init(): +0: entry P:0 S:0 + *i = 42:int + return + +`}, + } + for _, test := range tests { + // Create a single-file main package. + var conf loader.Config + f, err := conf.ParseFile("", test.input) + if err != nil { + t.Errorf("test %q: %s", test.input[:15], err) + continue + } + conf.CreateFromFiles(f.Name.Name, f) + + lprog, err := conf.Load() + if err != nil { + t.Errorf("test 'package %s': Load: %s", f.Name.Name, err) + continue + } + prog := ssautil.CreateProgram(lprog, test.mode) + mainPkg := prog.Package(lprog.Created[0].Pkg) + prog.Build() + initFunc := mainPkg.Func("init") + if initFunc == nil { + t.Errorf("test 'package %s': no init function", f.Name.Name) + continue + } + + var initbuf bytes.Buffer + _, err = initFunc.WriteTo(&initbuf) + if err != nil { + t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err) + continue + } + + if initbuf.String() != test.want { + t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want) + } + } +} + +// TestSyntheticFuncs checks that the expected synthetic functions are +// created, reachable, and not duplicated. +func TestSyntheticFuncs(t *testing.T) { + const input = `package P +type T int +func (T) f() int +func (*T) g() int +var ( + // thunks + a = T.f + b = T.f + c = (struct{T}).f + d = (struct{T}).f + e = (*T).g + f = (*T).g + g = (struct{*T}).g + h = (struct{*T}).g + + // bounds + i = T(0).f + j = T(0).f + k = new(T).g + l = new(T).g + + // wrappers + m interface{} = struct{T}{} + n interface{} = struct{T}{} + o interface{} = struct{*T}{} + p interface{} = struct{*T}{} + q interface{} = new(struct{T}) + r interface{} = new(struct{T}) + s interface{} = new(struct{*T}) + t interface{} = new(struct{*T}) +) +` + // Parse + var conf loader.Config + f, err := conf.ParseFile("", input) + if err != nil { + t.Fatalf("parse: %v", err) + } + conf.CreateFromFiles(f.Name.Name, f) + + // Load + lprog, err := conf.Load() + if err != nil { + t.Fatalf("Load: %v", err) + } + + // Create and build SSA + prog := ssautil.CreateProgram(lprog, 0) + prog.Build() + + // Enumerate reachable synthetic functions + want := map[string]string{ + "(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int", + "(P.T).f$bound": "bound method wrapper for func (P.T).f() int", + + "(*P.T).g$thunk": "thunk for func (*P.T).g() int", + "(P.T).f$thunk": "thunk for func (P.T).f() int", + "(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int", + "(struct{P.T}).f$thunk": "thunk for func (P.T).f() int", + + "(*P.T).f": "wrapper for func (P.T).f() int", + "(*struct{*P.T}).f": "wrapper for func (P.T).f() int", + "(*struct{*P.T}).g": "wrapper for func (*P.T).g() int", + "(*struct{P.T}).f": "wrapper for func (P.T).f() int", + "(*struct{P.T}).g": "wrapper for func (*P.T).g() int", + "(struct{*P.T}).f": "wrapper for func (P.T).f() int", + "(struct{*P.T}).g": "wrapper for func (*P.T).g() int", + "(struct{P.T}).f": "wrapper for func (P.T).f() int", + + "P.init": "package initializer", + } + for fn := range ssautil.AllFunctions(prog) { + if fn.Synthetic == "" { + continue + } + name := fn.String() + wantDescr, ok := want[name] + if !ok { + t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic) + continue + } + delete(want, name) + + if wantDescr != fn.Synthetic { + t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr) + } + } + for fn, descr := range want { + t.Errorf("want func: %q: %q", fn, descr) + } +} + +// TestPhiElimination ensures that dead phis, including those that +// participate in a cycle, are properly eliminated. +func TestPhiElimination(t *testing.T) { + const input = ` +package p + +func f() error + +func g(slice []int) { + for { + for range slice { + // e should not be lifted to a dead φ-node. + e := f() + h(e) + } + } +} + +func h(error) +` + // The SSA code for this function should look something like this: + // 0: + // jump 1 + // 1: + // t0 = len(slice) + // jump 2 + // 2: + // t1 = phi [1: -1:int, 3: t2] + // t2 = t1 + 1:int + // t3 = t2 < t0 + // if t3 goto 3 else 1 + // 3: + // t4 = f() + // t5 = h(t4) + // jump 2 + // + // But earlier versions of the SSA construction algorithm would + // additionally generate this cycle of dead phis: + // + // 1: + // t7 = phi [0: nil:error, 2: t8] #e + // ... + // 2: + // t8 = phi [1: t7, 3: t4] #e + // ... + + // Parse + var conf loader.Config + f, err := conf.ParseFile("", input) + if err != nil { + t.Fatalf("parse: %v", err) + } + conf.CreateFromFiles("p", f) + + // Load + lprog, err := conf.Load() + if err != nil { + t.Fatalf("Load: %v", err) + } + + // Create and build SSA + prog := ssautil.CreateProgram(lprog, 0) + p := prog.Package(lprog.Package("p").Pkg) + p.Build() + g := p.Func("g") + + phis := 0 + for _, b := range g.Blocks { + for _, instr := range b.Instrs { + if _, ok := instr.(*ssa.Phi); ok { + phis++ + } + } + } + if phis != 1 { + g.WriteTo(os.Stderr) + t.Errorf("expected a single Phi (for the range index), got %d", phis) + } +}