gitee.com/wgliang/goreporter@v0.0.0-20180902115603-df1b20f7c5d0/linters/simpler/ssa/builder_test.go (about)

     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  
     5  // +build go1.5
     6  
     7  package ssa_test
     8  
     9  import (
    10  	"bytes"
    11  	"go/ast"
    12  	"go/importer"
    13  	"go/parser"
    14  	"go/token"
    15  	"go/types"
    16  	"reflect"
    17  	"sort"
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/360EntSecGroup-Skylar/goreporter/linters/simpler/ssa"
    22  	"github.com/360EntSecGroup-Skylar/goreporter/linters/simpler/ssa/ssautil"
    23  	"golang.org/x/tools/go/loader"
    24  )
    25  
    26  func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
    27  
    28  // Tests that programs partially loaded from gc object files contain
    29  // functions with no code for the external portions, but are otherwise ok.
    30  func TestBuildPackage(t *testing.T) {
    31  	input := `
    32  package main
    33  
    34  import (
    35  	"bytes"
    36  	"io"
    37  	"testing"
    38  )
    39  
    40  func main() {
    41          var t testing.T
    42  	t.Parallel()    // static call to external declared method
    43          t.Fail()        // static call to promoted external declared method
    44          testing.Short() // static call to external package-level function
    45  
    46          var w io.Writer = new(bytes.Buffer)
    47          w.Write(nil)    // interface invoke of external declared method
    48  }
    49  `
    50  
    51  	// Parse the file.
    52  	fset := token.NewFileSet()
    53  	f, err := parser.ParseFile(fset, "input.go", input, 0)
    54  	if err != nil {
    55  		t.Error(err)
    56  		return
    57  	}
    58  
    59  	// Build an SSA program from the parsed file.
    60  	// Load its dependencies from gc binary export data.
    61  	mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
    62  		types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
    63  	if err != nil {
    64  		t.Error(err)
    65  		return
    66  	}
    67  
    68  	// The main package, its direct and indirect dependencies are loaded.
    69  	deps := []string{
    70  		// directly imported dependencies:
    71  		"bytes", "io", "testing",
    72  		// indirect dependencies mentioned by
    73  		// the direct imports' export data
    74  		"sync", "unicode", "time",
    75  	}
    76  
    77  	prog := mainPkg.Prog
    78  	all := prog.AllPackages()
    79  	if len(all) <= len(deps) {
    80  		t.Errorf("unexpected set of loaded packages: %q", all)
    81  	}
    82  	for _, path := range deps {
    83  		pkg := prog.ImportedPackage(path)
    84  		if pkg == nil {
    85  			t.Errorf("package not loaded: %q", path)
    86  			continue
    87  		}
    88  
    89  		// External packages should have no function bodies (except for wrappers).
    90  		isExt := pkg != mainPkg
    91  
    92  		// init()
    93  		if isExt && !isEmpty(pkg.Func("init")) {
    94  			t.Errorf("external package %s has non-empty init", pkg)
    95  		} else if !isExt && isEmpty(pkg.Func("init")) {
    96  			t.Errorf("main package %s has empty init", pkg)
    97  		}
    98  
    99  		for _, mem := range pkg.Members {
   100  			switch mem := mem.(type) {
   101  			case *ssa.Function:
   102  				// Functions at package level.
   103  				if isExt && !isEmpty(mem) {
   104  					t.Errorf("external function %s is non-empty", mem)
   105  				} else if !isExt && isEmpty(mem) {
   106  					t.Errorf("function %s is empty", mem)
   107  				}
   108  
   109  			case *ssa.Type:
   110  				// Methods of named types T.
   111  				// (In this test, all exported methods belong to *T not T.)
   112  				if !isExt {
   113  					t.Fatalf("unexpected name type in main package: %s", mem)
   114  				}
   115  				mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
   116  				for i, n := 0, mset.Len(); i < n; i++ {
   117  					m := prog.MethodValue(mset.At(i))
   118  					// For external types, only synthetic wrappers have code.
   119  					expExt := !strings.Contains(m.Synthetic, "wrapper")
   120  					if expExt && !isEmpty(m) {
   121  						t.Errorf("external method %s is non-empty: %s",
   122  							m, m.Synthetic)
   123  					} else if !expExt && isEmpty(m) {
   124  						t.Errorf("method function %s is empty: %s",
   125  							m, m.Synthetic)
   126  					}
   127  				}
   128  			}
   129  		}
   130  	}
   131  
   132  	expectedCallee := []string{
   133  		"(*testing.T).Parallel",
   134  		"(*testing.common).Fail",
   135  		"testing.Short",
   136  		"N/A",
   137  	}
   138  	callNum := 0
   139  	for _, b := range mainPkg.Func("main").Blocks {
   140  		for _, instr := range b.Instrs {
   141  			switch instr := instr.(type) {
   142  			case ssa.CallInstruction:
   143  				call := instr.Common()
   144  				if want := expectedCallee[callNum]; want != "N/A" {
   145  					got := call.StaticCallee().String()
   146  					if want != got {
   147  						t.Errorf("call #%d from main.main: got callee %s, want %s",
   148  							callNum, got, want)
   149  					}
   150  				}
   151  				callNum++
   152  			}
   153  		}
   154  	}
   155  	if callNum != 4 {
   156  		t.Errorf("in main.main: got %d calls, want %d", callNum, 4)
   157  	}
   158  }
   159  
   160  // TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.
   161  func TestRuntimeTypes(t *testing.T) {
   162  	tests := []struct {
   163  		input string
   164  		want  []string
   165  	}{
   166  		// An exported package-level type is needed.
   167  		{`package A; type T struct{}; func (T) f() {}`,
   168  			[]string{"*p.T", "p.T"},
   169  		},
   170  		// An unexported package-level type is not needed.
   171  		{`package B; type t struct{}; func (t) f() {}`,
   172  			nil,
   173  		},
   174  		// Subcomponents of type of exported package-level var are needed.
   175  		{`package C; import "bytes"; var V struct {*bytes.Buffer}`,
   176  			[]string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
   177  		},
   178  		// Subcomponents of type of unexported package-level var are not needed.
   179  		{`package D; import "bytes"; var v struct {*bytes.Buffer}`,
   180  			nil,
   181  		},
   182  		// Subcomponents of type of exported package-level function are needed.
   183  		{`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`,
   184  			[]string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
   185  		},
   186  		// Subcomponents of type of unexported package-level function are not needed.
   187  		{`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
   188  			nil,
   189  		},
   190  		// Subcomponents of type of exported method of uninstantiated unexported type are not needed.
   191  		{`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`,
   192  			nil,
   193  		},
   194  		// ...unless used by MakeInterface.
   195  		{`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
   196  			[]string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"},
   197  		},
   198  		// Subcomponents of type of unexported method are not needed.
   199  		{`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,
   200  			[]string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"},
   201  		},
   202  		// Local types aren't needed.
   203  		{`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
   204  			nil,
   205  		},
   206  		// ...unless used by MakeInterface.
   207  		{`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
   208  			[]string{"*bytes.Buffer", "*p.T", "p.T"},
   209  		},
   210  		// Types used as operand of MakeInterface are needed.
   211  		{`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
   212  			[]string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
   213  		},
   214  		// MakeInterface is optimized away when storing to a blank.
   215  		{`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
   216  			nil,
   217  		},
   218  	}
   219  	for _, test := range tests {
   220  		// Parse the file.
   221  		fset := token.NewFileSet()
   222  		f, err := parser.ParseFile(fset, "input.go", test.input, 0)
   223  		if err != nil {
   224  			t.Errorf("test %q: %s", test.input[:15], err)
   225  			continue
   226  		}
   227  
   228  		// Create a single-file main package.
   229  		// Load dependencies from gc binary export data.
   230  		ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
   231  			types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
   232  		if err != nil {
   233  			t.Errorf("test %q: %s", test.input[:15], err)
   234  			continue
   235  		}
   236  
   237  		var typstrs []string
   238  		for _, T := range ssapkg.Prog.RuntimeTypes() {
   239  			typstrs = append(typstrs, T.String())
   240  		}
   241  		sort.Strings(typstrs)
   242  
   243  		if !reflect.DeepEqual(typstrs, test.want) {
   244  			t.Errorf("test 'package %s': got %q, want %q",
   245  				f.Name.Name, typstrs, test.want)
   246  		}
   247  	}
   248  }
   249  
   250  // TestInit tests that synthesized init functions are correctly formed.
   251  // Bare init functions omit calls to dependent init functions and the use of
   252  // an init guard. They are useful in cases where the client uses a different
   253  // calling convention for init functions, or cases where it is easier for a
   254  // client to analyze bare init functions. Both of these aspects are used by
   255  // the llgo compiler for simpler integration with gccgo's runtime library,
   256  // and to simplify the analysis whereby it deduces which stores to globals
   257  // can be lowered to global initializers.
   258  func TestInit(t *testing.T) {
   259  	tests := []struct {
   260  		mode        ssa.BuilderMode
   261  		input, want string
   262  	}{
   263  		{0, `package A; import _ "errors"; var i int = 42`,
   264  			`# Name: A.init
   265  # Package: A
   266  # Synthetic: package initializer
   267  func init():
   268  0:                                                                entry P:0 S:2
   269  	t0 = *init$guard                                                   bool
   270  	if t0 goto 2 else 1
   271  1:                                                           init.start P:1 S:1
   272  	*init$guard = true:bool
   273  	t1 = errors.init()                                                   ()
   274  	*i = 42:int
   275  	jump 2
   276  2:                                                            init.done P:2 S:0
   277  	return
   278  
   279  `},
   280  		{ssa.BareInits, `package B; import _ "errors"; var i int = 42`,
   281  			`# Name: B.init
   282  # Package: B
   283  # Synthetic: package initializer
   284  func init():
   285  0:                                                                entry P:0 S:0
   286  	*i = 42:int
   287  	return
   288  
   289  `},
   290  	}
   291  	for _, test := range tests {
   292  		// Create a single-file main package.
   293  		var conf loader.Config
   294  		f, err := conf.ParseFile("<input>", test.input)
   295  		if err != nil {
   296  			t.Errorf("test %q: %s", test.input[:15], err)
   297  			continue
   298  		}
   299  		conf.CreateFromFiles(f.Name.Name, f)
   300  
   301  		lprog, err := conf.Load()
   302  		if err != nil {
   303  			t.Errorf("test 'package %s': Load: %s", f.Name.Name, err)
   304  			continue
   305  		}
   306  		prog := ssautil.CreateProgram(lprog, test.mode)
   307  		mainPkg := prog.Package(lprog.Created[0].Pkg)
   308  		prog.Build()
   309  		initFunc := mainPkg.Func("init")
   310  		if initFunc == nil {
   311  			t.Errorf("test 'package %s': no init function", f.Name.Name)
   312  			continue
   313  		}
   314  
   315  		var initbuf bytes.Buffer
   316  		_, err = initFunc.WriteTo(&initbuf)
   317  		if err != nil {
   318  			t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err)
   319  			continue
   320  		}
   321  
   322  		if initbuf.String() != test.want {
   323  			t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want)
   324  		}
   325  	}
   326  }
   327  
   328  // TestSyntheticFuncs checks that the expected synthetic functions are
   329  // created, reachable, and not duplicated.
   330  func TestSyntheticFuncs(t *testing.T) {
   331  	const input = `package P
   332  type T int
   333  func (T) f() int
   334  func (*T) g() int
   335  var (
   336  	// thunks
   337  	a = T.f
   338  	b = T.f
   339  	c = (struct{T}).f
   340  	d = (struct{T}).f
   341  	e = (*T).g
   342  	f = (*T).g
   343  	g = (struct{*T}).g
   344  	h = (struct{*T}).g
   345  
   346  	// bounds
   347  	i = T(0).f
   348  	j = T(0).f
   349  	k = new(T).g
   350  	l = new(T).g
   351  
   352  	// wrappers
   353  	m interface{} = struct{T}{}
   354  	n interface{} = struct{T}{}
   355  	o interface{} = struct{*T}{}
   356  	p interface{} = struct{*T}{}
   357  	q interface{} = new(struct{T})
   358  	r interface{} = new(struct{T})
   359  	s interface{} = new(struct{*T})
   360  	t interface{} = new(struct{*T})
   361  )
   362  `
   363  	// Parse
   364  	var conf loader.Config
   365  	f, err := conf.ParseFile("<input>", input)
   366  	if err != nil {
   367  		t.Fatalf("parse: %v", err)
   368  	}
   369  	conf.CreateFromFiles(f.Name.Name, f)
   370  
   371  	// Load
   372  	lprog, err := conf.Load()
   373  	if err != nil {
   374  		t.Fatalf("Load: %v", err)
   375  	}
   376  
   377  	// Create and build SSA
   378  	prog := ssautil.CreateProgram(lprog, 0)
   379  	prog.Build()
   380  
   381  	// Enumerate reachable synthetic functions
   382  	want := map[string]string{
   383  		"(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int",
   384  		"(P.T).f$bound":  "bound method wrapper for func (P.T).f() int",
   385  
   386  		"(*P.T).g$thunk":         "thunk for func (*P.T).g() int",
   387  		"(P.T).f$thunk":          "thunk for func (P.T).f() int",
   388  		"(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int",
   389  		"(struct{P.T}).f$thunk":  "thunk for func (P.T).f() int",
   390  
   391  		"(*P.T).f":          "wrapper for func (P.T).f() int",
   392  		"(*struct{*P.T}).f": "wrapper for func (P.T).f() int",
   393  		"(*struct{*P.T}).g": "wrapper for func (*P.T).g() int",
   394  		"(*struct{P.T}).f":  "wrapper for func (P.T).f() int",
   395  		"(*struct{P.T}).g":  "wrapper for func (*P.T).g() int",
   396  		"(struct{*P.T}).f":  "wrapper for func (P.T).f() int",
   397  		"(struct{*P.T}).g":  "wrapper for func (*P.T).g() int",
   398  		"(struct{P.T}).f":   "wrapper for func (P.T).f() int",
   399  
   400  		"P.init": "package initializer",
   401  	}
   402  	for fn := range ssautil.AllFunctions(prog) {
   403  		if fn.Synthetic == "" {
   404  			continue
   405  		}
   406  		name := fn.String()
   407  		wantDescr, ok := want[name]
   408  		if !ok {
   409  			t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic)
   410  			continue
   411  		}
   412  		delete(want, name)
   413  
   414  		if wantDescr != fn.Synthetic {
   415  			t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr)
   416  		}
   417  	}
   418  	for fn, descr := range want {
   419  		t.Errorf("want func: %q: %q", fn, descr)
   420  	}
   421  }