GoPLS Viewer

Home|gopls/go/pointer/query.go
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.
4
5package pointer
6
7import (
8    "errors"
9    "fmt"
10    "go/ast"
11    "go/parser"
12    "go/token"
13    "go/types"
14    "strconv"
15)
16
17// An extendedQuery represents a sequence of destructuring operations
18// applied to an ssa.Value (denoted by "x").
19type extendedQuery struct {
20    ops []interface{}
21    ptr *Pointer
22}
23
24// indexValue returns the value of an integer literal used as an
25// index.
26func indexValue(expr ast.Expr) (interror) {
27    litok := expr.(*ast.BasicLit)
28    if !ok {
29        return 0fmt.Errorf("non-integer index (%T)"expr)
30    }
31    if lit.Kind != token.INT {
32        return 0fmt.Errorf("non-integer index %s"lit.Value)
33    }
34    return strconv.Atoi(lit.Value)
35}
36
37// parseExtendedQuery parses and validates a destructuring Go
38// expression and returns the sequence of destructuring operations.
39// See parseDestructuringExpr for details.
40func parseExtendedQuery(typ types.Typequery string) ([]interface{}, types.Typeerror) {
41    exprerr := parser.ParseExpr(query)
42    if err != nil {
43        return nilnilerr
44    }
45    opstyperr := destructuringOps(typexpr)
46    if err != nil {
47        return nilnilerr
48    }
49    if len(ops) == 0 {
50        return nilnilerrors.New("invalid query: must not be empty")
51    }
52    if ops[0] != "x" {
53        return nilnilfmt.Errorf("invalid query: query operand must be named x")
54    }
55    if !CanPoint(typ) {
56        return nilnilfmt.Errorf("query does not describe a pointer-like value: %s"typ)
57    }
58    return opstypnil
59}
60
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.
65func destructuringOps(typ types.Typeexpr ast.Expr) ([]interface{}, types.Typeerror) {
66    switch expr := expr.(type) {
67    case *ast.SelectorExpr:
68        outtyperr := destructuringOps(typexpr.X)
69        if err != nil {
70            return nilnilerr
71        }
72
73        var structT *types.Struct
74        switch typ := typ.Underlying().(type) {
75        case *types.Pointer:
76            var ok bool
77            structTok = typ.Elem().Underlying().(*types.Struct)
78            if !ok {
79                return nilnilfmt.Errorf("cannot access field %s of pointer to type %s"expr.Sel.Nametyp.Elem())
80            }
81
82            out = append(out"load")
83        case *types.Struct:
84            structT = typ
85        default:
86            return nilnilfmt.Errorf("cannot access field %s of type %s"expr.Sel.Nametyp)
87        }
88
89        for i := 0i < structT.NumFields(); i++ {
90            field := structT.Field(i)
91            if field.Name() == expr.Sel.Name {
92                out = append(out"field"i)
93                return outfield.Type().Underlying(), nil
94            }
95        }
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 nilnilfmt.Errorf("no field %s in %s (embedded fields must be resolved manually)"expr.Sel.NamestructT)
105    case *ast.Ident:
106        return []interface{}{expr.Name}, typnil
107    case *ast.BasicLit:
108        return []interface{}{expr.Value}, nilnil
109    case *ast.IndexExpr:
110        outtyperr := destructuringOps(typexpr.X)
111        if err != nil {
112            return nilnilerr
113        }
114        switch typ := typ.Underlying().(type) {
115        case *types.Array:
116            out = append(out"arrayelem")
117            return outtyp.Elem().Underlying(), nil
118        case *types.Slice:
119            out = append(out"sliceelem")
120            return outtyp.Elem().Underlying(), nil
121        case *types.Map:
122            out = append(out"mapelem")
123            return outtyp.Elem().Underlying(), nil
124        case *types.Tuple:
125            out = append(out"index")
126            idxerr := indexValue(expr.Index)
127            if err != nil {
128                return nilnilerr
129            }
130            out = append(outidx)
131            if idx >= typ.Len() || idx < 0 {
132                return nilnilfmt.Errorf("tuple index %d out of bounds"idx)
133            }
134            return outtyp.At(idx).Type().Underlying(), nil
135        default:
136            return nilnilfmt.Errorf("cannot index type %s"typ)
137        }
138
139    case *ast.UnaryExpr:
140        if expr.Op != token.ARROW {
141            return nilnilfmt.Errorf("unsupported unary operator %s"expr.Op)
142        }
143        outtyperr := destructuringOps(typexpr.X)
144        if err != nil {
145            return nilnilerr
146        }
147        chok := typ.(*types.Chan)
148        if !ok {
149            return nilnilfmt.Errorf("cannot receive from value of type %s"typ)
150        }
151        out = append(out"recv")
152        return outch.Elem().Underlying(), err
153    case *ast.ParenExpr:
154        return destructuringOps(typexpr.X)
155    case *ast.StarExpr:
156        outtyperr := destructuringOps(typexpr.X)
157        if err != nil {
158            return nilnilerr
159        }
160        ptrok := typ.(*types.Pointer)
161        if !ok {
162            return nilnilfmt.Errorf("cannot dereference type %s"typ)
163        }
164        out = append(out"load")
165        return outptr.Elem().Underlying(), err
166    default:
167        return nilnilfmt.Errorf("unsupported expression %T"expr)
168    }
169}
170
171func (a *analysisevalExtendedQuery(t types.Typeid nodeidops []interface{}) (types.Typenodeid) {
172    pid := id
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 := 1i < len(ops); i++ {
177        var nid nodeid
178        switch ops[i] {
179        case "recv":
180            t = t.(*types.Chan).Elem().Underlying()
181            nid = a.addNodes(t"query.extended")
182            a.load(nidpid0a.sizeof(t))
183        case "field":
184            i++ // fetch field index
185            tt := t.(*types.Struct)
186            idx := ops[i].(int)
187            offset := a.offsetOf(tidx)
188            t = tt.Field(idx).Type().Underlying()
189            nid = a.addNodes(t"query.extended")
190            a.copy(nidpid+nodeid(offset), a.sizeof(t))
191        case "arrayelem":
192            t = t.(*types.Array).Elem().Underlying()
193            nid = a.addNodes(t"query.extended")
194            a.copy(nid1+pida.sizeof(t))
195        case "sliceelem":
196            t = t.(*types.Slice).Elem().Underlying()
197            nid = a.addNodes(t"query.extended")
198            a.load(nidpid1a.sizeof(t))
199        case "mapelem":
200            tt := t.(*types.Map)
201            t = tt.Elem()
202            ksize := a.sizeof(tt.Key())
203            vsize := a.sizeof(tt.Elem())
204            nid = a.addNodes(t"query.extended")
205            a.load(nidpidksizevsize)
206        case "index":
207            i++ // fetch index
208            tt := t.(*types.Tuple)
209            idx := ops[i].(int)
210            t = tt.At(idx).Type().Underlying()
211            nid = a.addNodes(t"query.extended")
212            a.copy(nidpid+nodeid(idx), a.sizeof(t))
213        case "load":
214            t = t.(*types.Pointer).Elem().Underlying()
215            nid = a.addNodes(t"query.extended")
216            a.load(nidpid0a.sizeof(t))
217        default:
218            // shouldn't happen
219            panic(fmt.Sprintf("unknown op %q"ops[i]))
220        }
221        pid = nid
222    }
223
224    return tpid
225}
226
MembersX
parseExtendedQuery.ops
destructuringOps.BlockStmt.BlockStmt.idx
analysis.evalExtendedQuery
analysis.evalExtendedQuery.id
destructuringOps.BlockStmt.BlockStmt.field
analysis.evalExtendedQuery.i
analysis.evalExtendedQuery.BlockStmt.BlockStmt.ksize
ast
extendedQuery.ops
indexValue.expr
parseExtendedQuery.query
destructuringOps.expr
destructuringOps.BlockStmt.typ
destructuringOps.BlockStmt.err
analysis.evalExtendedQuery.a
extendedQuery.ptr
destructuringOps
destructuringOps.BlockStmt.out
destructuringOps.BlockStmt.structT
destructuringOps.BlockStmt.i
analysis.evalExtendedQuery.BlockStmt.BlockStmt.vsize
parseExtendedQuery.typ
parseExtendedQuery.expr
parseExtendedQuery.err
analysis.evalExtendedQuery.BlockStmt.nid
parser
indexValue
parseExtendedQuery
destructuringOps.typ
destructuringOps.BlockStmt.BlockStmt.ok
destructuringOps.BlockStmt.BlockStmt.err
analysis.evalExtendedQuery.ops
extendedQuery
analysis.evalExtendedQuery.t
analysis.evalExtendedQuery.pid
analysis.evalExtendedQuery.BlockStmt.BlockStmt.offset
Members
X