Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / go / pointer / query.go
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.Underlying().(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.Underlying().(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 }