github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/oracle/pointsto14.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 oracle 8 9 import ( 10 "fmt" 11 "go/ast" 12 "go/token" 13 "sort" 14 15 "golang.org/x/tools/go/ast/astutil" 16 "golang.org/x/tools/go/loader" 17 "golang.org/x/tools/go/pointer" 18 "golang.org/x/tools/go/ssa" 19 "golang.org/x/tools/go/ssa/ssautil" 20 "golang.org/x/tools/go/types" 21 "golang.org/x/tools/oracle/serial" 22 ) 23 24 // pointsto runs the pointer analysis on the selected expression, 25 // and reports its points-to set (for a pointer-like expression) 26 // or its dynamic types (for an interface, reflect.Value, or 27 // reflect.Type expression) and their points-to sets. 28 // 29 // All printed sets are sorted to ensure determinism. 30 // 31 func pointsto(q *Query) error { 32 lconf := loader.Config{Build: q.Build} 33 34 if err := setPTAScope(&lconf, q.Scope); err != nil { 35 return err 36 } 37 38 // Load/parse/type-check the program. 39 lprog, err := lconf.Load() 40 if err != nil { 41 return err 42 } 43 q.Fset = lprog.Fset 44 45 qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos 46 if err != nil { 47 return err 48 } 49 50 prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) 51 52 ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) 53 if err != nil { 54 return err 55 } 56 57 path, action := findInterestingNode(qpos.info, qpos.path) 58 if action != actionExpr { 59 return fmt.Errorf("pointer analysis wants an expression; got %s", 60 astutil.NodeDescription(qpos.path[0])) 61 } 62 63 var expr ast.Expr 64 var obj types.Object 65 switch n := path[0].(type) { 66 case *ast.ValueSpec: 67 // ambiguous ValueSpec containing multiple names 68 return fmt.Errorf("multiple value specification") 69 case *ast.Ident: 70 obj = qpos.info.ObjectOf(n) 71 expr = n 72 case ast.Expr: 73 expr = n 74 default: 75 // TODO(adonovan): is this reachable? 76 return fmt.Errorf("unexpected AST for expr: %T", n) 77 } 78 79 // Reject non-pointerlike types (includes all constants---except nil). 80 // TODO(adonovan): reject nil too. 81 typ := qpos.info.TypeOf(expr) 82 if !pointer.CanPoint(typ) { 83 return fmt.Errorf("pointer analysis wants an expression of reference type; got %s", typ) 84 } 85 86 // Determine the ssa.Value for the expression. 87 var value ssa.Value 88 var isAddr bool 89 if obj != nil { 90 // def/ref of func/var object 91 value, isAddr, err = ssaValueForIdent(prog, qpos.info, obj, path) 92 } else { 93 value, isAddr, err = ssaValueForExpr(prog, qpos.info, path) 94 } 95 if err != nil { 96 return err // e.g. trivially dead code 97 } 98 99 // Defer SSA construction till after errors are reported. 100 prog.Build() 101 102 // Run the pointer analysis. 103 ptrs, err := runPTA(ptaConfig, value, isAddr) 104 if err != nil { 105 return err // e.g. analytically unreachable 106 } 107 108 q.result = &pointstoResult{ 109 qpos: qpos, 110 typ: typ, 111 ptrs: ptrs, 112 } 113 return nil 114 } 115 116 // ssaValueForIdent returns the ssa.Value for the ast.Ident whose path 117 // to the root of the AST is path. isAddr reports whether the 118 // ssa.Value is the address denoted by the ast.Ident, not its value. 119 // 120 func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) { 121 switch obj := obj.(type) { 122 case *types.Var: 123 pkg := prog.Package(qinfo.Pkg) 124 pkg.Build() 125 if v, addr := prog.VarValue(obj, pkg, path); v != nil { 126 return v, addr, nil 127 } 128 return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name()) 129 130 case *types.Func: 131 fn := prog.FuncValue(obj) 132 if fn == nil { 133 return nil, false, fmt.Errorf("%s is an interface method", obj) 134 } 135 // TODO(adonovan): there's no point running PTA on a *Func ident. 136 // Eliminate this feature. 137 return fn, false, nil 138 } 139 panic(obj) 140 } 141 142 // ssaValueForExpr returns the ssa.Value of the non-ast.Ident 143 // expression whose path to the root of the AST is path. 144 // 145 func ssaValueForExpr(prog *ssa.Program, qinfo *loader.PackageInfo, path []ast.Node) (value ssa.Value, isAddr bool, err error) { 146 pkg := prog.Package(qinfo.Pkg) 147 pkg.SetDebugMode(true) 148 pkg.Build() 149 150 fn := ssa.EnclosingFunction(pkg, path) 151 if fn == nil { 152 return nil, false, fmt.Errorf("no SSA function built for this location (dead code?)") 153 } 154 155 if v, addr := fn.ValueForExpr(path[0].(ast.Expr)); v != nil { 156 return v, addr, nil 157 } 158 159 return nil, false, fmt.Errorf("can't locate SSA Value for expression in %s", fn) 160 } 161 162 // runPTA runs the pointer analysis of the selected SSA value or address. 163 func runPTA(conf *pointer.Config, v ssa.Value, isAddr bool) (ptrs []pointerResult, err error) { 164 T := v.Type() 165 if isAddr { 166 conf.AddIndirectQuery(v) 167 T = deref(T) 168 } else { 169 conf.AddQuery(v) 170 } 171 ptares := ptrAnalysis(conf) 172 173 var ptr pointer.Pointer 174 if isAddr { 175 ptr = ptares.IndirectQueries[v] 176 } else { 177 ptr = ptares.Queries[v] 178 } 179 if ptr == (pointer.Pointer{}) { 180 return nil, fmt.Errorf("pointer analysis did not find expression (dead code?)") 181 } 182 pts := ptr.PointsTo() 183 184 if pointer.CanHaveDynamicTypes(T) { 185 // Show concrete types for interface/reflect.Value expression. 186 if concs := pts.DynamicTypes(); concs.Len() > 0 { 187 concs.Iterate(func(conc types.Type, pta interface{}) { 188 labels := pta.(pointer.PointsToSet).Labels() 189 sort.Sort(byPosAndString(labels)) // to ensure determinism 190 ptrs = append(ptrs, pointerResult{conc, labels}) 191 }) 192 } 193 } else { 194 // Show labels for other expressions. 195 labels := pts.Labels() 196 sort.Sort(byPosAndString(labels)) // to ensure determinism 197 ptrs = append(ptrs, pointerResult{T, labels}) 198 } 199 sort.Sort(byTypeString(ptrs)) // to ensure determinism 200 return ptrs, nil 201 } 202 203 type pointerResult struct { 204 typ types.Type // type of the pointer (always concrete) 205 labels []*pointer.Label // set of labels 206 } 207 208 type pointstoResult struct { 209 qpos *queryPos 210 typ types.Type // type of expression 211 ptrs []pointerResult // pointer info (typ is concrete => len==1) 212 } 213 214 func (r *pointstoResult) display(printf printfFunc) { 215 if pointer.CanHaveDynamicTypes(r.typ) { 216 // Show concrete types for interface, reflect.Type or 217 // reflect.Value expression. 218 219 if len(r.ptrs) > 0 { 220 printf(r.qpos, "this %s may contain these dynamic types:", r.qpos.typeString(r.typ)) 221 for _, ptr := range r.ptrs { 222 var obj types.Object 223 if nt, ok := deref(ptr.typ).(*types.Named); ok { 224 obj = nt.Obj() 225 } 226 if len(ptr.labels) > 0 { 227 printf(obj, "\t%s, may point to:", r.qpos.typeString(ptr.typ)) 228 printLabels(printf, ptr.labels, "\t\t") 229 } else { 230 printf(obj, "\t%s", r.qpos.typeString(ptr.typ)) 231 } 232 } 233 } else { 234 printf(r.qpos, "this %s cannot contain any dynamic types.", r.typ) 235 } 236 } else { 237 // Show labels for other expressions. 238 if ptr := r.ptrs[0]; len(ptr.labels) > 0 { 239 printf(r.qpos, "this %s may point to these objects:", 240 r.qpos.typeString(r.typ)) 241 printLabels(printf, ptr.labels, "\t") 242 } else { 243 printf(r.qpos, "this %s may not point to anything.", 244 r.qpos.typeString(r.typ)) 245 } 246 } 247 } 248 249 func (r *pointstoResult) toSerial(res *serial.Result, fset *token.FileSet) { 250 var pts []serial.PointsTo 251 for _, ptr := range r.ptrs { 252 var namePos string 253 if nt, ok := deref(ptr.typ).(*types.Named); ok { 254 namePos = fset.Position(nt.Obj().Pos()).String() 255 } 256 var labels []serial.PointsToLabel 257 for _, l := range ptr.labels { 258 labels = append(labels, serial.PointsToLabel{ 259 Pos: fset.Position(l.Pos()).String(), 260 Desc: l.String(), 261 }) 262 } 263 pts = append(pts, serial.PointsTo{ 264 Type: r.qpos.typeString(ptr.typ), 265 NamePos: namePos, 266 Labels: labels, 267 }) 268 } 269 res.PointsTo = pts 270 } 271 272 type byTypeString []pointerResult 273 274 func (a byTypeString) Len() int { return len(a) } 275 func (a byTypeString) Less(i, j int) bool { return a[i].typ.String() < a[j].typ.String() } 276 func (a byTypeString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 277 278 type byPosAndString []*pointer.Label 279 280 func (a byPosAndString) Len() int { return len(a) } 281 func (a byPosAndString) Less(i, j int) bool { 282 cmp := a[i].Pos() - a[j].Pos() 283 return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String()) 284 } 285 func (a byPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 286 287 func printLabels(printf printfFunc, labels []*pointer.Label, prefix string) { 288 // TODO(adonovan): due to context-sensitivity, many of these 289 // labels may differ only by context, which isn't apparent. 290 for _, label := range labels { 291 printf(label, "%s%s", prefix, label) 292 } 293 }