// valPos tries to find a position for the given value // even if the value doesn't have one. func valPos(val ssa.Value) token.Pos { if pos := val.Pos(); pos.IsValid() { return pos } instr, ok := val.(ssa.Instruction) if !ok { return token.NoPos } for _, op := range instr.Operands(nil) { if pos := (*op).Pos(); pos.IsValid() { return pos } } return token.NoPos }
func (ctxt *context) getErrorInfo(v ssa.Value, member int, enclosingPos token.Pos, seen map[ssa.Value]bool) (result *errorInfo) { if !enclosingPos.IsValid() { panicf("getErrorInfo with invalid pos; %T %s", v, v) } // log.Printf("getErrorInfo[%d] %T %v {", member, v, v) // defer func() { // log.Printf("} -> %+v", result) // }() if seen[v] { return &errorInfo{} } seen[v] = true defer delete(seen, v) if pos := v.Pos(); pos.IsValid() { enclosingPos = pos } terminate := func() []errorTermination { return []errorTermination{{ val: v, pos: enclosingPos, }} } switch v := v.(type) { case *ssa.Call: if member > 0 && member != v.Type().(*types.Tuple).Len()-1 { log.Printf("error from non-final member of function") return &errorInfo{unknown: terminate()} } return &errorInfo{nested: []*ssa.Call{v}} case *ssa.ChangeInterface: return ctxt.getErrorInfo(v.X, 0, enclosingPos, seen) case *ssa.Extract: return ctxt.getErrorInfo(v.Tuple, v.Index, enclosingPos, seen) case *ssa.Field: return &errorInfo{unknown: terminate()} case *ssa.Index: return &errorInfo{unknown: terminate()} case *ssa.Lookup: return &errorInfo{unknown: terminate()} case *ssa.Const: if v.Value != nil { panicf("non-nil constant cannot make error, surely?") } return &errorInfo{} case *ssa.MakeInterface: // TODO look into components of v.X return &errorInfo{nonNil: terminate()} case *ssa.Next: return &errorInfo{unknown: terminate()} case *ssa.Parameter: return &errorInfo{unknown: terminate()} case *ssa.Phi: var info errorInfo for _, edge := range v.Edges { info.add(ctxt.getErrorInfo(edge, member, enclosingPos, seen)) } return &info case *ssa.Select: return &errorInfo{unknown: terminate()} case *ssa.TypeAssert: if v.CommaOk { return &errorInfo{unknown: terminate()} } return ctxt.getErrorInfo(v.X, 0, enclosingPos, seen) case *ssa.UnOp: switch v.Op { case token.ARROW: return &errorInfo{unknown: terminate()} case token.MUL: if _, isGlobal := v.X.(*ssa.Global); isGlobal { // Assume that if we're returning a global variable, it's a // global non-nil error, such as os.ErrInvalid. return &errorInfo{nonNil: terminate()} } return &errorInfo{unknown: terminate()} default: panicf("unexpected unary operator %s at %s", v, ctxt.lprog.Fset.Position(enclosingPos)) } } panicf("unexpected value found for error: %T; %v", v, v) panic("not reached") }