github.com/pankona/gometalinter@v2.0.11+incompatible/_linters/src/golang.org/x/tools/go/pointer/query.go (about) 1 package pointer 2 3 import ( 4 "errors" 5 "fmt" 6 "go/ast" 7 "go/parser" 8 "go/token" 9 "go/types" 10 "strconv" 11 ) 12 13 // An extendedQuery represents a sequence of destructuring operations 14 // applied to an ssa.Value (denoted by "x"). 15 type extendedQuery struct { 16 ops []interface{} 17 ptr *Pointer 18 } 19 20 // indexValue returns the value of an integer literal used as an 21 // index. 22 func indexValue(expr ast.Expr) (int, error) { 23 lit, ok := expr.(*ast.BasicLit) 24 if !ok { 25 return 0, fmt.Errorf("non-integer index (%T)", expr) 26 } 27 if lit.Kind != token.INT { 28 return 0, fmt.Errorf("non-integer index %s", lit.Value) 29 } 30 return strconv.Atoi(lit.Value) 31 } 32 33 // parseExtendedQuery parses and validates a destructuring Go 34 // expression and returns the sequence of destructuring operations. 35 // See parseDestructuringExpr for details. 36 func parseExtendedQuery(typ types.Type, query string) ([]interface{}, types.Type, error) { 37 expr, err := parser.ParseExpr(query) 38 if err != nil { 39 return nil, nil, err 40 } 41 ops, typ, err := destructuringOps(typ, expr) 42 if err != nil { 43 return nil, nil, err 44 } 45 if len(ops) == 0 { 46 return nil, nil, errors.New("invalid query: must not be empty") 47 } 48 if ops[0] != "x" { 49 return nil, nil, fmt.Errorf("invalid query: query operand must be named x") 50 } 51 if !CanPoint(typ) { 52 return nil, nil, fmt.Errorf("query does not describe a pointer-like value: %s", typ) 53 } 54 return ops, typ, nil 55 } 56 57 // destructuringOps parses a Go expression consisting only of an 58 // identifier "x", field selections, indexing, channel receives, load 59 // operations and parens---for example: "<-(*x[i])[key]"--- and 60 // returns the sequence of destructuring operations on x. 61 func destructuringOps(typ types.Type, expr ast.Expr) ([]interface{}, types.Type, error) { 62 switch expr := expr.(type) { 63 case *ast.SelectorExpr: 64 out, typ, err := destructuringOps(typ, expr.X) 65 if err != nil { 66 return nil, nil, err 67 } 68 69 var structT *types.Struct 70 switch typ := typ.(type) { 71 case *types.Pointer: 72 var ok bool 73 structT, ok = typ.Elem().Underlying().(*types.Struct) 74 if !ok { 75 return nil, nil, fmt.Errorf("cannot access field %s of pointer to type %s", expr.Sel.Name, typ.Elem()) 76 } 77 78 out = append(out, "load") 79 case *types.Struct: 80 structT = typ 81 default: 82 return nil, nil, fmt.Errorf("cannot access field %s of type %s", expr.Sel.Name, typ) 83 } 84 85 for i := 0; i < structT.NumFields(); i++ { 86 field := structT.Field(i) 87 if field.Name() == expr.Sel.Name { 88 out = append(out, "field", i) 89 return out, field.Type().Underlying(), nil 90 } 91 } 92 // TODO(dh): supporting embedding would need something like 93 // types.LookupFieldOrMethod, but without taking package 94 // boundaries into account, because we may want to access 95 // unexported fields. If we were only interested in one level 96 // of unexported name, we could determine the appropriate 97 // package and run LookupFieldOrMethod with that. However, a 98 // single query may want to cross multiple package boundaries, 99 // and at this point it's not really worth the complexity. 100 return nil, nil, fmt.Errorf("no field %s in %s (embedded fields must be resolved manually)", expr.Sel.Name, structT) 101 case *ast.Ident: 102 return []interface{}{expr.Name}, typ, nil 103 case *ast.BasicLit: 104 return []interface{}{expr.Value}, nil, nil 105 case *ast.IndexExpr: 106 out, typ, err := destructuringOps(typ, expr.X) 107 if err != nil { 108 return nil, nil, err 109 } 110 switch typ := typ.(type) { 111 case *types.Array: 112 out = append(out, "arrayelem") 113 return out, typ.Elem().Underlying(), nil 114 case *types.Slice: 115 out = append(out, "sliceelem") 116 return out, typ.Elem().Underlying(), nil 117 case *types.Map: 118 out = append(out, "mapelem") 119 return out, typ.Elem().Underlying(), nil 120 case *types.Tuple: 121 out = append(out, "index") 122 idx, err := indexValue(expr.Index) 123 if err != nil { 124 return nil, nil, err 125 } 126 out = append(out, idx) 127 if idx >= typ.Len() || idx < 0 { 128 return nil, nil, fmt.Errorf("tuple index %d out of bounds", idx) 129 } 130 return out, typ.At(idx).Type().Underlying(), nil 131 default: 132 return nil, nil, fmt.Errorf("cannot index type %s", typ) 133 } 134 135 case *ast.UnaryExpr: 136 if expr.Op != token.ARROW { 137 return nil, nil, fmt.Errorf("unsupported unary operator %s", expr.Op) 138 } 139 out, typ, err := destructuringOps(typ, expr.X) 140 if err != nil { 141 return nil, nil, err 142 } 143 ch, ok := typ.(*types.Chan) 144 if !ok { 145 return nil, nil, fmt.Errorf("cannot receive from value of type %s", typ) 146 } 147 out = append(out, "recv") 148 return out, ch.Elem().Underlying(), err 149 case *ast.ParenExpr: 150 return destructuringOps(typ, expr.X) 151 case *ast.StarExpr: 152 out, typ, err := destructuringOps(typ, expr.X) 153 if err != nil { 154 return nil, nil, err 155 } 156 ptr, ok := typ.(*types.Pointer) 157 if !ok { 158 return nil, nil, fmt.Errorf("cannot dereference type %s", typ) 159 } 160 out = append(out, "load") 161 return out, ptr.Elem().Underlying(), err 162 default: 163 return nil, nil, fmt.Errorf("unsupported expression %T", expr) 164 } 165 } 166 167 func (a *analysis) evalExtendedQuery(t types.Type, id nodeid, ops []interface{}) (types.Type, nodeid) { 168 pid := id 169 // TODO(dh): we're allocating intermediary nodes each time 170 // evalExtendedQuery is called. We should probably only generate 171 // them once per (v, ops) pair. 172 for i := 1; i < len(ops); i++ { 173 var nid nodeid 174 switch ops[i] { 175 case "recv": 176 t = t.(*types.Chan).Elem().Underlying() 177 nid = a.addNodes(t, "query.extended") 178 a.load(nid, pid, 0, a.sizeof(t)) 179 case "field": 180 i++ // fetch field index 181 tt := t.(*types.Struct) 182 idx := ops[i].(int) 183 offset := a.offsetOf(t, idx) 184 t = tt.Field(idx).Type().Underlying() 185 nid = a.addNodes(t, "query.extended") 186 a.copy(nid, pid+nodeid(offset), a.sizeof(t)) 187 case "arrayelem": 188 t = t.(*types.Array).Elem().Underlying() 189 nid = a.addNodes(t, "query.extended") 190 a.copy(nid, 1+pid, a.sizeof(t)) 191 case "sliceelem": 192 t = t.(*types.Slice).Elem().Underlying() 193 nid = a.addNodes(t, "query.extended") 194 a.load(nid, pid, 1, a.sizeof(t)) 195 case "mapelem": 196 tt := t.(*types.Map) 197 t = tt.Elem() 198 ksize := a.sizeof(tt.Key()) 199 vsize := a.sizeof(tt.Elem()) 200 nid = a.addNodes(t, "query.extended") 201 a.load(nid, pid, ksize, vsize) 202 case "index": 203 i++ // fetch index 204 tt := t.(*types.Tuple) 205 idx := ops[i].(int) 206 t = tt.At(idx).Type().Underlying() 207 nid = a.addNodes(t, "query.extended") 208 a.copy(nid, pid+nodeid(idx), a.sizeof(t)) 209 case "load": 210 t = t.(*types.Pointer).Elem().Underlying() 211 nid = a.addNodes(t, "query.extended") 212 a.load(nid, pid, 0, a.sizeof(t)) 213 default: 214 // shouldn't happen 215 panic(fmt.Sprintf("unknown op %q", ops[i])) 216 } 217 pid = nid 218 } 219 220 return t, pid 221 }