1 // Copyright 2017 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.
17 // An extendedQuery represents a sequence of destructuring operations
18 // applied to an ssa.Value (denoted by "x").
19 type extendedQuery struct {
24 // indexValue returns the value of an integer literal used as an
26 func indexValue(expr ast.Expr) (int, error) {
27 lit, ok := expr.(*ast.BasicLit)
29 return 0, fmt.Errorf("non-integer index (%T)", expr)
31 if lit.Kind != token.INT {
32 return 0, fmt.Errorf("non-integer index %s", lit.Value)
34 return strconv.Atoi(lit.Value)
37 // parseExtendedQuery parses and validates a destructuring Go
38 // expression and returns the sequence of destructuring operations.
39 // See parseDestructuringExpr for details.
40 func parseExtendedQuery(typ types.Type, query string) ([]interface{}, types.Type, error) {
41 expr, err := parser.ParseExpr(query)
45 ops, typ, err := destructuringOps(typ, expr)
50 return nil, nil, errors.New("invalid query: must not be empty")
53 return nil, nil, fmt.Errorf("invalid query: query operand must be named x")
56 return nil, nil, fmt.Errorf("query does not describe a pointer-like value: %s", typ)
61 // destructuringOps parses a Go expression consisting only of an
62 // identifier "x", field selections, indexing, channel receives, load
63 // operations and parens---for example: "<-(*x[i])[key]"--- and
64 // returns the sequence of destructuring operations on x.
65 func destructuringOps(typ types.Type, expr ast.Expr) ([]interface{}, types.Type, error) {
66 switch expr := expr.(type) {
67 case *ast.SelectorExpr:
68 out, typ, err := destructuringOps(typ, expr.X)
73 var structT *types.Struct
74 switch typ := typ.Underlying().(type) {
77 structT, ok = typ.Elem().Underlying().(*types.Struct)
79 return nil, nil, fmt.Errorf("cannot access field %s of pointer to type %s", expr.Sel.Name, typ.Elem())
82 out = append(out, "load")
86 return nil, nil, fmt.Errorf("cannot access field %s of type %s", expr.Sel.Name, typ)
89 for i := 0; i < structT.NumFields(); i++ {
90 field := structT.Field(i)
91 if field.Name() == expr.Sel.Name {
92 out = append(out, "field", i)
93 return out, field.Type().Underlying(), nil
96 // TODO(dh): supporting embedding would need something like
97 // types.LookupFieldOrMethod, but without taking package
98 // boundaries into account, because we may want to access
99 // unexported fields. If we were only interested in one level
100 // of unexported name, we could determine the appropriate
101 // package and run LookupFieldOrMethod with that. However, a
102 // single query may want to cross multiple package boundaries,
103 // and at this point it's not really worth the complexity.
104 return nil, nil, fmt.Errorf("no field %s in %s (embedded fields must be resolved manually)", expr.Sel.Name, structT)
106 return []interface{}{expr.Name}, typ, nil
108 return []interface{}{expr.Value}, nil, nil
110 out, typ, err := destructuringOps(typ, expr.X)
114 switch typ := typ.Underlying().(type) {
116 out = append(out, "arrayelem")
117 return out, typ.Elem().Underlying(), nil
119 out = append(out, "sliceelem")
120 return out, typ.Elem().Underlying(), nil
122 out = append(out, "mapelem")
123 return out, typ.Elem().Underlying(), nil
125 out = append(out, "index")
126 idx, err := indexValue(expr.Index)
130 out = append(out, idx)
131 if idx >= typ.Len() || idx < 0 {
132 return nil, nil, fmt.Errorf("tuple index %d out of bounds", idx)
134 return out, typ.At(idx).Type().Underlying(), nil
136 return nil, nil, fmt.Errorf("cannot index type %s", typ)
140 if expr.Op != token.ARROW {
141 return nil, nil, fmt.Errorf("unsupported unary operator %s", expr.Op)
143 out, typ, err := destructuringOps(typ, expr.X)
147 ch, ok := typ.(*types.Chan)
149 return nil, nil, fmt.Errorf("cannot receive from value of type %s", typ)
151 out = append(out, "recv")
152 return out, ch.Elem().Underlying(), err
154 return destructuringOps(typ, expr.X)
156 out, typ, err := destructuringOps(typ, expr.X)
160 ptr, ok := typ.(*types.Pointer)
162 return nil, nil, fmt.Errorf("cannot dereference type %s", typ)
164 out = append(out, "load")
165 return out, ptr.Elem().Underlying(), err
167 return nil, nil, fmt.Errorf("unsupported expression %T", expr)
171 func (a *analysis) evalExtendedQuery(t types.Type, id nodeid, ops []interface{}) (types.Type, nodeid) {
173 // TODO(dh): we're allocating intermediary nodes each time
174 // evalExtendedQuery is called. We should probably only generate
175 // them once per (v, ops) pair.
176 for i := 1; i < len(ops); i++ {
180 t = t.(*types.Chan).Elem().Underlying()
181 nid = a.addNodes(t, "query.extended")
182 a.load(nid, pid, 0, a.sizeof(t))
184 i++ // fetch field index
185 tt := t.(*types.Struct)
187 offset := a.offsetOf(t, idx)
188 t = tt.Field(idx).Type().Underlying()
189 nid = a.addNodes(t, "query.extended")
190 a.copy(nid, pid+nodeid(offset), a.sizeof(t))
192 t = t.(*types.Array).Elem().Underlying()
193 nid = a.addNodes(t, "query.extended")
194 a.copy(nid, 1+pid, a.sizeof(t))
196 t = t.(*types.Slice).Elem().Underlying()
197 nid = a.addNodes(t, "query.extended")
198 a.load(nid, pid, 1, a.sizeof(t))
202 ksize := a.sizeof(tt.Key())
203 vsize := a.sizeof(tt.Elem())
204 nid = a.addNodes(t, "query.extended")
205 a.load(nid, pid, ksize, vsize)
208 tt := t.(*types.Tuple)
210 t = tt.At(idx).Type().Underlying()
211 nid = a.addNodes(t, "query.extended")
212 a.copy(nid, pid+nodeid(idx), a.sizeof(t))
214 t = t.(*types.Pointer).Elem().Underlying()
215 nid = a.addNodes(t, "query.extended")
216 a.load(nid, pid, 0, a.sizeof(t))
219 panic(fmt.Sprintf("unknown op %q", ops[i]))