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 }