github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gotools/go/ssa/source_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  package ssa_test
     6  
     7  // This file defines tests of source-level debugging utilities.
     8  
     9  import (
    10  	"fmt"
    11  	"go/ast"
    12  	"go/parser"
    13  	"go/token"
    14  	"os"
    15  	"regexp"
    16  	"strings"
    17  	"testing"
    18  
    19  	"llvm.org/llgo/third_party/gotools/go/ast/astutil"
    20  	"llvm.org/llgo/third_party/gotools/go/exact"
    21  	"llvm.org/llgo/third_party/gotools/go/loader"
    22  	"llvm.org/llgo/third_party/gotools/go/ssa"
    23  	"llvm.org/llgo/third_party/gotools/go/types"
    24  )
    25  
    26  func TestObjValueLookup(t *testing.T) {
    27  	conf := loader.Config{ParserMode: parser.ParseComments}
    28  	f, err := conf.ParseFile("testdata/objlookup.go", nil)
    29  	if err != nil {
    30  		t.Error(err)
    31  		return
    32  	}
    33  	conf.CreateFromFiles("main", f)
    34  
    35  	// Maps each var Ident (represented "name:linenum") to the
    36  	// kind of ssa.Value we expect (represented "Constant", "&Alloc").
    37  	expectations := make(map[string]string)
    38  
    39  	// Find all annotations of form x::BinOp, &y::Alloc, etc.
    40  	re := regexp.MustCompile(`(\b|&)?(\w*)::(\w*)\b`)
    41  	for _, c := range f.Comments {
    42  		text := c.Text()
    43  		pos := conf.Fset.Position(c.Pos())
    44  		for _, m := range re.FindAllStringSubmatch(text, -1) {
    45  			key := fmt.Sprintf("%s:%d", m[2], pos.Line)
    46  			value := m[1] + m[3]
    47  			expectations[key] = value
    48  		}
    49  	}
    50  
    51  	iprog, err := conf.Load()
    52  	if err != nil {
    53  		t.Error(err)
    54  		return
    55  	}
    56  
    57  	prog := ssa.Create(iprog, 0 /*|ssa.PrintFunctions*/)
    58  	mainInfo := iprog.Created[0]
    59  	mainPkg := prog.Package(mainInfo.Pkg)
    60  	mainPkg.SetDebugMode(true)
    61  	mainPkg.Build()
    62  
    63  	var varIds []*ast.Ident
    64  	var varObjs []*types.Var
    65  	for id, obj := range mainInfo.Defs {
    66  		// Check invariants for func and const objects.
    67  		switch obj := obj.(type) {
    68  		case *types.Func:
    69  			checkFuncValue(t, prog, obj)
    70  
    71  		case *types.Const:
    72  			checkConstValue(t, prog, obj)
    73  
    74  		case *types.Var:
    75  			if id.Name == "_" {
    76  				continue
    77  			}
    78  			varIds = append(varIds, id)
    79  			varObjs = append(varObjs, obj)
    80  		}
    81  	}
    82  	for id, obj := range mainInfo.Uses {
    83  		if obj, ok := obj.(*types.Var); ok {
    84  			varIds = append(varIds, id)
    85  			varObjs = append(varObjs, obj)
    86  		}
    87  	}
    88  
    89  	// Check invariants for var objects.
    90  	// The result varies based on the specific Ident.
    91  	for i, id := range varIds {
    92  		obj := varObjs[i]
    93  		ref, _ := astutil.PathEnclosingInterval(f, id.Pos(), id.Pos())
    94  		pos := prog.Fset.Position(id.Pos())
    95  		exp := expectations[fmt.Sprintf("%s:%d", id.Name, pos.Line)]
    96  		if exp == "" {
    97  			t.Errorf("%s: no expectation for var ident %s ", pos, id.Name)
    98  			continue
    99  		}
   100  		wantAddr := false
   101  		if exp[0] == '&' {
   102  			wantAddr = true
   103  			exp = exp[1:]
   104  		}
   105  		checkVarValue(t, prog, mainPkg, ref, obj, exp, wantAddr)
   106  	}
   107  }
   108  
   109  func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) {
   110  	fn := prog.FuncValue(obj)
   111  	// fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging
   112  	if fn == nil {
   113  		if obj.Name() != "interfaceMethod" {
   114  			t.Errorf("FuncValue(%s) == nil", obj)
   115  		}
   116  		return
   117  	}
   118  	if fnobj := fn.Object(); fnobj != obj {
   119  		t.Errorf("FuncValue(%s).Object() == %s; value was %s",
   120  			obj, fnobj, fn.Name())
   121  		return
   122  	}
   123  	if !types.Identical(fn.Type(), obj.Type()) {
   124  		t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type())
   125  		return
   126  	}
   127  }
   128  
   129  func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) {
   130  	c := prog.ConstValue(obj)
   131  	// fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging
   132  	if c == nil {
   133  		t.Errorf("ConstValue(%s) == nil", obj)
   134  		return
   135  	}
   136  	if !types.Identical(c.Type(), obj.Type()) {
   137  		t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type())
   138  		return
   139  	}
   140  	if obj.Name() != "nil" {
   141  		if !exact.Compare(c.Value, token.EQL, obj.Val()) {
   142  			t.Errorf("ConstValue(%s).Value (%s) != %s",
   143  				obj, c.Value, obj.Val())
   144  			return
   145  		}
   146  	}
   147  }
   148  
   149  func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) {
   150  	// The prefix of all assertions messages.
   151  	prefix := fmt.Sprintf("VarValue(%s @ L%d)",
   152  		obj, prog.Fset.Position(ref[0].Pos()).Line)
   153  
   154  	v, gotAddr := prog.VarValue(obj, pkg, ref)
   155  
   156  	// Kind is the concrete type of the ssa Value.
   157  	gotKind := "nil"
   158  	if v != nil {
   159  		gotKind = fmt.Sprintf("%T", v)[len("*ssa."):]
   160  	}
   161  
   162  	// fmt.Printf("%s = %v (kind %q; expect %q) wantAddr=%t gotAddr=%t\n", prefix, v, gotKind, expKind, wantAddr, gotAddr) // debugging
   163  
   164  	// Check the kinds match.
   165  	// "nil" indicates expected failure (e.g. optimized away).
   166  	if expKind != gotKind {
   167  		t.Errorf("%s concrete type == %s, want %s", prefix, gotKind, expKind)
   168  	}
   169  
   170  	// Check the types match.
   171  	// If wantAddr, the expected type is the object's address.
   172  	if v != nil {
   173  		expType := obj.Type()
   174  		if wantAddr {
   175  			expType = types.NewPointer(expType)
   176  			if !gotAddr {
   177  				t.Errorf("%s: got value, want address", prefix)
   178  			}
   179  		} else if gotAddr {
   180  			t.Errorf("%s: got address, want value", prefix)
   181  		}
   182  		if !types.Identical(v.Type(), expType) {
   183  			t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType)
   184  		}
   185  	}
   186  }
   187  
   188  // Ensure that, in debug mode, we can determine the ssa.Value
   189  // corresponding to every ast.Expr.
   190  func TestValueForExpr(t *testing.T) {
   191  	conf := loader.Config{ParserMode: parser.ParseComments}
   192  	f, err := conf.ParseFile("testdata/valueforexpr.go", nil)
   193  	if err != nil {
   194  		t.Error(err)
   195  		return
   196  	}
   197  	conf.CreateFromFiles("main", f)
   198  
   199  	iprog, err := conf.Load()
   200  	if err != nil {
   201  		t.Error(err)
   202  		return
   203  	}
   204  
   205  	mainInfo := iprog.Created[0]
   206  
   207  	prog := ssa.Create(iprog, 0)
   208  	mainPkg := prog.Package(mainInfo.Pkg)
   209  	mainPkg.SetDebugMode(true)
   210  	mainPkg.Build()
   211  
   212  	if false {
   213  		// debugging
   214  		for _, mem := range mainPkg.Members {
   215  			if fn, ok := mem.(*ssa.Function); ok {
   216  				fn.WriteTo(os.Stderr)
   217  			}
   218  		}
   219  	}
   220  
   221  	// Find the actual AST node for each canonical position.
   222  	parenExprByPos := make(map[token.Pos]*ast.ParenExpr)
   223  	ast.Inspect(f, func(n ast.Node) bool {
   224  		if n != nil {
   225  			if e, ok := n.(*ast.ParenExpr); ok {
   226  				parenExprByPos[e.Pos()] = e
   227  			}
   228  		}
   229  		return true
   230  	})
   231  
   232  	// Find all annotations of form /*@kind*/.
   233  	for _, c := range f.Comments {
   234  		text := strings.TrimSpace(c.Text())
   235  		if text == "" || text[0] != '@' {
   236  			continue
   237  		}
   238  		text = text[1:]
   239  		pos := c.End() + 1
   240  		position := prog.Fset.Position(pos)
   241  		var e ast.Expr
   242  		if target := parenExprByPos[pos]; target == nil {
   243  			t.Errorf("%s: annotation doesn't precede ParenExpr: %q", position, text)
   244  			continue
   245  		} else {
   246  			e = target.X
   247  		}
   248  
   249  		path, _ := astutil.PathEnclosingInterval(f, pos, pos)
   250  		if path == nil {
   251  			t.Errorf("%s: can't find AST path from root to comment: %s", position, text)
   252  			continue
   253  		}
   254  
   255  		fn := ssa.EnclosingFunction(mainPkg, path)
   256  		if fn == nil {
   257  			t.Errorf("%s: can't find enclosing function", position)
   258  			continue
   259  		}
   260  
   261  		v, gotAddr := fn.ValueForExpr(e) // (may be nil)
   262  		got := strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa.")
   263  		if want := text; got != want {
   264  			t.Errorf("%s: got value %q, want %q", position, got, want)
   265  		}
   266  		if v != nil {
   267  			T := v.Type()
   268  			if gotAddr {
   269  				T = T.Underlying().(*types.Pointer).Elem() // deref
   270  			}
   271  			if !types.Identical(T, mainInfo.TypeOf(e)) {
   272  				t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T)
   273  			}
   274  		}
   275  	}
   276  }