github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/oracle/whicherrs14.go (about) 1 // Copyright 2014 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/ssa" 18 "golang.org/x/tools/go/ssa/ssautil" 19 "golang.org/x/tools/go/types" 20 "golang.org/x/tools/oracle/serial" 21 ) 22 23 var builtinErrorType = types.Universe.Lookup("error").Type() 24 25 // whicherrs takes an position to an error and tries to find all types, constants 26 // and global value which a given error can point to and which can be checked from the 27 // scope where the error lives. 28 // In short, it returns a list of things that can be checked against in order to handle 29 // an error properly. 30 // 31 // TODO(dmorsing): figure out if fields in errors like *os.PathError.Err 32 // can be queried recursively somehow. 33 func whicherrs(q *Query) error { 34 lconf := loader.Config{Build: q.Build} 35 36 if err := setPTAScope(&lconf, q.Scope); err != nil { 37 return err 38 } 39 40 // Load/parse/type-check the program. 41 lprog, err := lconf.Load() 42 if err != nil { 43 return err 44 } 45 q.Fset = lprog.Fset 46 47 qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos 48 if err != nil { 49 return err 50 } 51 52 prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) 53 54 ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) 55 if err != nil { 56 return err 57 } 58 59 path, action := findInterestingNode(qpos.info, qpos.path) 60 if action != actionExpr { 61 return fmt.Errorf("whicherrs wants an expression; got %s", 62 astutil.NodeDescription(qpos.path[0])) 63 } 64 var expr ast.Expr 65 var obj types.Object 66 switch n := path[0].(type) { 67 case *ast.ValueSpec: 68 // ambiguous ValueSpec containing multiple names 69 return fmt.Errorf("multiple value specification") 70 case *ast.Ident: 71 obj = qpos.info.ObjectOf(n) 72 expr = n 73 case ast.Expr: 74 expr = n 75 default: 76 return fmt.Errorf("unexpected AST for expr: %T", n) 77 } 78 79 typ := qpos.info.TypeOf(expr) 80 if !types.Identical(typ, builtinErrorType) { 81 return fmt.Errorf("selection is not an expression of type 'error'") 82 } 83 // Determine the ssa.Value for the expression. 84 var value ssa.Value 85 if obj != nil { 86 // def/ref of func/var object 87 value, _, err = ssaValueForIdent(prog, qpos.info, obj, path) 88 } else { 89 value, _, err = ssaValueForExpr(prog, qpos.info, path) 90 } 91 if err != nil { 92 return err // e.g. trivially dead code 93 } 94 95 // Defer SSA construction till after errors are reported. 96 prog.Build() 97 98 globals := findVisibleErrs(prog, qpos) 99 constants := findVisibleConsts(prog, qpos) 100 101 res := &whicherrsResult{ 102 qpos: qpos, 103 errpos: expr.Pos(), 104 } 105 106 // TODO(adonovan): the following code is heavily duplicated 107 // w.r.t. "pointsto". Refactor? 108 109 // Find the instruction which initialized the 110 // global error. If more than one instruction has stored to the global 111 // remove the global from the set of values that we want to query. 112 allFuncs := ssautil.AllFunctions(prog) 113 for fn := range allFuncs { 114 for _, b := range fn.Blocks { 115 for _, instr := range b.Instrs { 116 store, ok := instr.(*ssa.Store) 117 if !ok { 118 continue 119 } 120 gval, ok := store.Addr.(*ssa.Global) 121 if !ok { 122 continue 123 } 124 gbl, ok := globals[gval] 125 if !ok { 126 continue 127 } 128 // we already found a store to this global 129 // The normal error define is just one store in the init 130 // so we just remove this global from the set we want to query 131 if gbl != nil { 132 delete(globals, gval) 133 } 134 globals[gval] = store.Val 135 } 136 } 137 } 138 139 ptaConfig.AddQuery(value) 140 for _, v := range globals { 141 ptaConfig.AddQuery(v) 142 } 143 144 ptares := ptrAnalysis(ptaConfig) 145 valueptr := ptares.Queries[value] 146 for g, v := range globals { 147 ptr, ok := ptares.Queries[v] 148 if !ok { 149 continue 150 } 151 if !ptr.MayAlias(valueptr) { 152 continue 153 } 154 res.globals = append(res.globals, g) 155 } 156 pts := valueptr.PointsTo() 157 dedup := make(map[*ssa.NamedConst]bool) 158 for _, label := range pts.Labels() { 159 // These values are either MakeInterfaces or reflect 160 // generated interfaces. For the purposes of this 161 // analysis, we don't care about reflect generated ones 162 makeiface, ok := label.Value().(*ssa.MakeInterface) 163 if !ok { 164 continue 165 } 166 constval, ok := makeiface.X.(*ssa.Const) 167 if !ok { 168 continue 169 } 170 c := constants[*constval] 171 if c != nil && !dedup[c] { 172 dedup[c] = true 173 res.consts = append(res.consts, c) 174 } 175 } 176 concs := pts.DynamicTypes() 177 concs.Iterate(func(conc types.Type, _ interface{}) { 178 // go/types is a bit annoying here. 179 // We want to find all the types that we can 180 // typeswitch or assert to. This means finding out 181 // if the type pointed to can be seen by us. 182 // 183 // For the purposes of this analysis, the type is always 184 // either a Named type or a pointer to one. 185 // There are cases where error can be implemented 186 // by unnamed types, but in that case, we can't assert to 187 // it, so we don't care about it for this analysis. 188 var name *types.TypeName 189 switch t := conc.(type) { 190 case *types.Pointer: 191 named, ok := t.Elem().(*types.Named) 192 if !ok { 193 return 194 } 195 name = named.Obj() 196 case *types.Named: 197 name = t.Obj() 198 default: 199 return 200 } 201 if !isAccessibleFrom(name, qpos.info.Pkg) { 202 return 203 } 204 res.types = append(res.types, &errorType{conc, name}) 205 }) 206 sort.Sort(membersByPosAndString(res.globals)) 207 sort.Sort(membersByPosAndString(res.consts)) 208 sort.Sort(sorterrorType(res.types)) 209 210 q.result = res 211 return nil 212 } 213 214 // findVisibleErrs returns a mapping from each package-level variable of type "error" to nil. 215 func findVisibleErrs(prog *ssa.Program, qpos *queryPos) map[*ssa.Global]ssa.Value { 216 globals := make(map[*ssa.Global]ssa.Value) 217 for _, pkg := range prog.AllPackages() { 218 for _, mem := range pkg.Members { 219 gbl, ok := mem.(*ssa.Global) 220 if !ok { 221 continue 222 } 223 gbltype := gbl.Type() 224 // globals are always pointers 225 if !types.Identical(deref(gbltype), builtinErrorType) { 226 continue 227 } 228 if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) { 229 continue 230 } 231 globals[gbl] = nil 232 } 233 } 234 return globals 235 } 236 237 // findVisibleConsts returns a mapping from each package-level constant assignable to type "error", to nil. 238 func findVisibleConsts(prog *ssa.Program, qpos *queryPos) map[ssa.Const]*ssa.NamedConst { 239 constants := make(map[ssa.Const]*ssa.NamedConst) 240 for _, pkg := range prog.AllPackages() { 241 for _, mem := range pkg.Members { 242 obj, ok := mem.(*ssa.NamedConst) 243 if !ok { 244 continue 245 } 246 consttype := obj.Type() 247 if !types.AssignableTo(consttype, builtinErrorType) { 248 continue 249 } 250 if !isAccessibleFrom(obj.Object(), qpos.info.Pkg) { 251 continue 252 } 253 constants[*obj.Value] = obj 254 } 255 } 256 257 return constants 258 } 259 260 type membersByPosAndString []ssa.Member 261 262 func (a membersByPosAndString) Len() int { return len(a) } 263 func (a membersByPosAndString) Less(i, j int) bool { 264 cmp := a[i].Pos() - a[j].Pos() 265 return cmp < 0 || cmp == 0 && a[i].String() < a[j].String() 266 } 267 func (a membersByPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 268 269 type sorterrorType []*errorType 270 271 func (a sorterrorType) Len() int { return len(a) } 272 func (a sorterrorType) Less(i, j int) bool { 273 cmp := a[i].obj.Pos() - a[j].obj.Pos() 274 return cmp < 0 || cmp == 0 && a[i].typ.String() < a[j].typ.String() 275 } 276 func (a sorterrorType) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 277 278 type errorType struct { 279 typ types.Type // concrete type N or *N that implements error 280 obj *types.TypeName // the named type N 281 } 282 283 type whicherrsResult struct { 284 qpos *queryPos 285 errpos token.Pos 286 globals []ssa.Member 287 consts []ssa.Member 288 types []*errorType 289 } 290 291 func (r *whicherrsResult) display(printf printfFunc) { 292 if len(r.globals) > 0 { 293 printf(r.qpos, "this error may point to these globals:") 294 for _, g := range r.globals { 295 printf(g.Pos(), "\t%s", g.RelString(r.qpos.info.Pkg)) 296 } 297 } 298 if len(r.consts) > 0 { 299 printf(r.qpos, "this error may contain these constants:") 300 for _, c := range r.consts { 301 printf(c.Pos(), "\t%s", c.RelString(r.qpos.info.Pkg)) 302 } 303 } 304 if len(r.types) > 0 { 305 printf(r.qpos, "this error may contain these dynamic types:") 306 for _, t := range r.types { 307 printf(t.obj.Pos(), "\t%s", r.qpos.typeString(t.typ)) 308 } 309 } 310 } 311 312 func (r *whicherrsResult) toSerial(res *serial.Result, fset *token.FileSet) { 313 we := &serial.WhichErrs{} 314 we.ErrPos = fset.Position(r.errpos).String() 315 for _, g := range r.globals { 316 we.Globals = append(we.Globals, fset.Position(g.Pos()).String()) 317 } 318 for _, c := range r.consts { 319 we.Constants = append(we.Constants, fset.Position(c.Pos()).String()) 320 } 321 for _, t := range r.types { 322 var et serial.WhichErrsType 323 et.Type = r.qpos.typeString(t.typ) 324 et.Position = fset.Position(t.obj.Pos()).String() 325 we.Types = append(we.Types, et) 326 } 327 res.WhichErrs = we 328 }