1 | // Copyright 2018 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 | |
5 | // Package objectpath defines a naming scheme for types.Objects |
6 | // (that is, named entities in Go programs) relative to their enclosing |
7 | // package. |
8 | // |
9 | // Type-checker objects are canonical, so they are usually identified by |
10 | // their address in memory (a pointer), but a pointer has meaning only |
11 | // within one address space. By contrast, objectpath names allow the |
12 | // identity of an object to be sent from one program to another, |
13 | // establishing a correspondence between types.Object variables that are |
14 | // distinct but logically equivalent. |
15 | // |
16 | // A single object may have multiple paths. In this example, |
17 | // |
18 | // type A struct{ X int } |
19 | // type B A |
20 | // |
21 | // the field X has two paths due to its membership of both A and B. |
22 | // The For(obj) function always returns one of these paths, arbitrarily |
23 | // but consistently. |
24 | package objectpath |
25 | |
26 | import ( |
27 | "fmt" |
28 | "go/types" |
29 | "sort" |
30 | "strconv" |
31 | "strings" |
32 | |
33 | "golang.org/x/tools/internal/typeparams" |
34 | ) |
35 | |
36 | // A Path is an opaque name that identifies a types.Object |
37 | // relative to its package. Conceptually, the name consists of a |
38 | // sequence of destructuring operations applied to the package scope |
39 | // to obtain the original object. |
40 | // The name does not include the package itself. |
41 | type Path string |
42 | |
43 | // Encoding |
44 | // |
45 | // An object path is a textual and (with training) human-readable encoding |
46 | // of a sequence of destructuring operators, starting from a types.Package. |
47 | // The sequences represent a path through the package/object/type graph. |
48 | // We classify these operators by their type: |
49 | // |
50 | // PO package->object Package.Scope.Lookup |
51 | // OT object->type Object.Type |
52 | // TT type->type Type.{Elem,Key,Params,Results,Underlying} [EKPRU] |
53 | // TO type->object Type.{At,Field,Method,Obj} [AFMO] |
54 | // |
55 | // All valid paths start with a package and end at an object |
56 | // and thus may be defined by the regular language: |
57 | // |
58 | // objectpath = PO (OT TT* TO)* |
59 | // |
60 | // The concrete encoding follows directly: |
61 | // - The only PO operator is Package.Scope.Lookup, which requires an identifier. |
62 | // - The only OT operator is Object.Type, |
63 | // which we encode as '.' because dot cannot appear in an identifier. |
64 | // - The TT operators are encoded as [EKPRUTC]; |
65 | // one of these (TypeParam) requires an integer operand, |
66 | // which is encoded as a string of decimal digits. |
67 | // - The TO operators are encoded as [AFMO]; |
68 | // three of these (At,Field,Method) require an integer operand, |
69 | // which is encoded as a string of decimal digits. |
70 | // These indices are stable across different representations |
71 | // of the same package, even source and export data. |
72 | // The indices used are implementation specific and may not correspond to |
73 | // the argument to the go/types function. |
74 | // |
75 | // In the example below, |
76 | // |
77 | // package p |
78 | // |
79 | // type T interface { |
80 | // f() (a string, b struct{ X int }) |
81 | // } |
82 | // |
83 | // field X has the path "T.UM0.RA1.F0", |
84 | // representing the following sequence of operations: |
85 | // |
86 | // p.Lookup("T") T |
87 | // .Type().Underlying().Method(0). f |
88 | // .Type().Results().At(1) b |
89 | // .Type().Field(0) X |
90 | // |
91 | // The encoding is not maximally compact---every R or P is |
92 | // followed by an A, for example---but this simplifies the |
93 | // encoder and decoder. |
94 | const ( |
95 | // object->type operators |
96 | opType = '.' // .Type() (Object) |
97 | |
98 | // type->type operators |
99 | opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map) |
100 | opKey = 'K' // .Key() (Map) |
101 | opParams = 'P' // .Params() (Signature) |
102 | opResults = 'R' // .Results() (Signature) |
103 | opUnderlying = 'U' // .Underlying() (Named) |
104 | opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature) |
105 | opConstraint = 'C' // .Constraint() (TypeParam) |
106 | |
107 | // type->object operators |
108 | opAt = 'A' // .At(i) (Tuple) |
109 | opField = 'F' // .Field(i) (Struct) |
110 | opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored) |
111 | opObj = 'O' // .Obj() (Named, TypeParam) |
112 | ) |
113 | |
114 | // The For function returns the path to an object relative to its package, |
115 | // or an error if the object is not accessible from the package's Scope. |
116 | // |
117 | // The For function guarantees to return a path only for the following objects: |
118 | // - package-level types |
119 | // - exported package-level non-types |
120 | // - methods |
121 | // - parameter and result variables |
122 | // - struct fields |
123 | // These objects are sufficient to define the API of their package. |
124 | // The objects described by a package's export data are drawn from this set. |
125 | // |
126 | // For does not return a path for predeclared names, imported package |
127 | // names, local names, and unexported package-level names (except |
128 | // types). |
129 | // |
130 | // Example: given this definition, |
131 | // |
132 | // package p |
133 | // |
134 | // type T interface { |
135 | // f() (a string, b struct{ X int }) |
136 | // } |
137 | // |
138 | // For(X) would return a path that denotes the following sequence of operations: |
139 | // |
140 | // p.Scope().Lookup("T") (TypeName T) |
141 | // .Type().Underlying().Method(0). (method Func f) |
142 | // .Type().Results().At(1) (field Var b) |
143 | // .Type().Field(0) (field Var X) |
144 | // |
145 | // where p is the package (*types.Package) to which X belongs. |
146 | func For(obj types.Object) (Path, error) { |
147 | pkg := obj.Pkg() |
148 | |
149 | // This table lists the cases of interest. |
150 | // |
151 | // Object Action |
152 | // ------ ------ |
153 | // nil reject |
154 | // builtin reject |
155 | // pkgname reject |
156 | // label reject |
157 | // var |
158 | // package-level accept |
159 | // func param/result accept |
160 | // local reject |
161 | // struct field accept |
162 | // const |
163 | // package-level accept |
164 | // local reject |
165 | // func |
166 | // package-level accept |
167 | // init functions reject |
168 | // concrete method accept |
169 | // interface method accept |
170 | // type |
171 | // package-level accept |
172 | // local reject |
173 | // |
174 | // The only accessible package-level objects are members of pkg itself. |
175 | // |
176 | // The cases are handled in four steps: |
177 | // |
178 | // 1. reject nil and builtin |
179 | // 2. accept package-level objects |
180 | // 3. reject obviously invalid objects |
181 | // 4. search the API for the path to the param/result/field/method. |
182 | |
183 | // 1. reference to nil or builtin? |
184 | if pkg == nil { |
185 | return "", fmt.Errorf("predeclared %s has no path", obj) |
186 | } |
187 | scope := pkg.Scope() |
188 | |
189 | // 2. package-level object? |
190 | if scope.Lookup(obj.Name()) == obj { |
191 | // Only exported objects (and non-exported types) have a path. |
192 | // Non-exported types may be referenced by other objects. |
193 | if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() { |
194 | return "", fmt.Errorf("no path for non-exported %v", obj) |
195 | } |
196 | return Path(obj.Name()), nil |
197 | } |
198 | |
199 | // 3. Not a package-level object. |
200 | // Reject obviously non-viable cases. |
201 | switch obj := obj.(type) { |
202 | case *types.TypeName: |
203 | if _, ok := obj.Type().(*typeparams.TypeParam); !ok { |
204 | // With the exception of type parameters, only package-level type names |
205 | // have a path. |
206 | return "", fmt.Errorf("no path for %v", obj) |
207 | } |
208 | case *types.Const, // Only package-level constants have a path. |
209 | *types.Label, // Labels are function-local. |
210 | *types.PkgName: // PkgNames are file-local. |
211 | return "", fmt.Errorf("no path for %v", obj) |
212 | |
213 | case *types.Var: |
214 | // Could be: |
215 | // - a field (obj.IsField()) |
216 | // - a func parameter or result |
217 | // - a local var. |
218 | // Sadly there is no way to distinguish |
219 | // a param/result from a local |
220 | // so we must proceed to the find. |
221 | |
222 | case *types.Func: |
223 | // A func, if not package-level, must be a method. |
224 | if recv := obj.Type().(*types.Signature).Recv(); recv == nil { |
225 | return "", fmt.Errorf("func is not a method: %v", obj) |
226 | } |
227 | |
228 | if path, ok := concreteMethod(obj); ok { |
229 | // Fast path for concrete methods that avoids looping over scope. |
230 | return path, nil |
231 | } |
232 | |
233 | default: |
234 | panic(obj) |
235 | } |
236 | |
237 | // 4. Search the API for the path to the var (field/param/result) or method. |
238 | |
239 | // First inspect package-level named types. |
240 | // In the presence of path aliases, these give |
241 | // the best paths because non-types may |
242 | // refer to types, but not the reverse. |
243 | empty := make([]byte, 0, 48) // initial space |
244 | names := scope.Names() |
245 | for _, name := range names { |
246 | o := scope.Lookup(name) |
247 | tname, ok := o.(*types.TypeName) |
248 | if !ok { |
249 | continue // handle non-types in second pass |
250 | } |
251 | |
252 | path := append(empty, name...) |
253 | path = append(path, opType) |
254 | |
255 | T := o.Type() |
256 | |
257 | if tname.IsAlias() { |
258 | // type alias |
259 | if r := find(obj, T, path, nil); r != nil { |
260 | return Path(r), nil |
261 | } |
262 | } else { |
263 | if named, _ := T.(*types.Named); named != nil { |
264 | if r := findTypeParam(obj, typeparams.ForNamed(named), path, nil); r != nil { |
265 | // generic named type |
266 | return Path(r), nil |
267 | } |
268 | } |
269 | // defined (named) type |
270 | if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil { |
271 | return Path(r), nil |
272 | } |
273 | } |
274 | } |
275 | |
276 | // Then inspect everything else: |
277 | // non-types, and declared methods of defined types. |
278 | for _, name := range names { |
279 | o := scope.Lookup(name) |
280 | path := append(empty, name...) |
281 | if _, ok := o.(*types.TypeName); !ok { |
282 | if o.Exported() { |
283 | // exported non-type (const, var, func) |
284 | if r := find(obj, o.Type(), append(path, opType), nil); r != nil { |
285 | return Path(r), nil |
286 | } |
287 | } |
288 | continue |
289 | } |
290 | |
291 | // Inspect declared methods of defined types. |
292 | if T, ok := o.Type().(*types.Named); ok { |
293 | path = append(path, opType) |
294 | // Note that method index here is always with respect |
295 | // to canonical ordering of methods, regardless of how |
296 | // they appear in the underlying type. |
297 | canonical := canonicalize(T) |
298 | for i := 0; i < len(canonical); i++ { |
299 | m := canonical[i] |
300 | path2 := appendOpArg(path, opMethod, i) |
301 | if m == obj { |
302 | return Path(path2), nil // found declared method |
303 | } |
304 | if r := find(obj, m.Type(), append(path2, opType), nil); r != nil { |
305 | return Path(r), nil |
306 | } |
307 | } |
308 | } |
309 | } |
310 | |
311 | return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path()) |
312 | } |
313 | |
314 | func appendOpArg(path []byte, op byte, arg int) []byte { |
315 | path = append(path, op) |
316 | path = strconv.AppendInt(path, int64(arg), 10) |
317 | return path |
318 | } |
319 | |
320 | // concreteMethod returns the path for meth, which must have a non-nil receiver. |
321 | // The second return value indicates success and may be false if the method is |
322 | // an interface method or if it is an instantiated method. |
323 | // |
324 | // This function is just an optimization that avoids the general scope walking |
325 | // approach. You are expected to fall back to the general approach if this |
326 | // function fails. |
327 | func concreteMethod(meth *types.Func) (Path, bool) { |
328 | // Concrete methods can only be declared on package-scoped named types. For |
329 | // that reason we can skip the expensive walk over the package scope: the |
330 | // path will always be package -> named type -> method. We can trivially get |
331 | // the type name from the receiver, and only have to look over the type's |
332 | // methods to find the method index. |
333 | // |
334 | // Methods on generic types require special consideration, however. Consider |
335 | // the following package: |
336 | // |
337 | // L1: type S[T any] struct{} |
338 | // L2: func (recv S[A]) Foo() { recv.Bar() } |
339 | // L3: func (recv S[B]) Bar() { } |
340 | // L4: type Alias = S[int] |
341 | // L5: func _[T any]() { var s S[int]; s.Foo() } |
342 | // |
343 | // The receivers of methods on generic types are instantiations. L2 and L3 |
344 | // instantiate S with the type-parameters A and B, which are scoped to the |
345 | // respective methods. L4 and L5 each instantiate S with int. Each of these |
346 | // instantiations has its own method set, full of methods (and thus objects) |
347 | // with receivers whose types are the respective instantiations. In other |
348 | // words, we have |
349 | // |
350 | // S[A].Foo, S[A].Bar |
351 | // S[B].Foo, S[B].Bar |
352 | // S[int].Foo, S[int].Bar |
353 | // |
354 | // We may thus be trying to produce object paths for any of these objects. |
355 | // |
356 | // S[A].Foo and S[B].Bar are the origin methods, and their paths are S.Foo |
357 | // and S.Bar, which are the paths that this function naturally produces. |
358 | // |
359 | // S[A].Bar, S[B].Foo, and both methods on S[int] are instantiations that |
360 | // don't correspond to the origin methods. For S[int], this is significant. |
361 | // The most precise object path for S[int].Foo, for example, is Alias.Foo, |
362 | // not S.Foo. Our function, however, would produce S.Foo, which would |
363 | // resolve to a different object. |
364 | // |
365 | // For S[A].Bar and S[B].Foo it could be argued that S.Bar and S.Foo are |
366 | // still the correct paths, since only the origin methods have meaningful |
367 | // paths. But this is likely only true for trivial cases and has edge cases. |
368 | // Since this function is only an optimization, we err on the side of giving |
369 | // up, deferring to the slower but definitely correct algorithm. Most users |
370 | // of objectpath will only be giving us origin methods, anyway, as referring |
371 | // to instantiated methods is usually not useful. |
372 | |
373 | if typeparams.OriginMethod(meth) != meth { |
374 | return "", false |
375 | } |
376 | |
377 | recvT := meth.Type().(*types.Signature).Recv().Type() |
378 | if ptr, ok := recvT.(*types.Pointer); ok { |
379 | recvT = ptr.Elem() |
380 | } |
381 | |
382 | named, ok := recvT.(*types.Named) |
383 | if !ok { |
384 | return "", false |
385 | } |
386 | |
387 | if types.IsInterface(named) { |
388 | // Named interfaces don't have to be package-scoped |
389 | // |
390 | // TODO(dominikh): opt: if scope.Lookup(name) == named, then we can apply this optimization to interface |
391 | // methods, too, I think. |
392 | return "", false |
393 | } |
394 | |
395 | // Preallocate space for the name, opType, opMethod, and some digits. |
396 | name := named.Obj().Name() |
397 | path := make([]byte, 0, len(name)+8) |
398 | path = append(path, name...) |
399 | path = append(path, opType) |
400 | canonical := canonicalize(named) |
401 | for i, m := range canonical { |
402 | if m == meth { |
403 | path = appendOpArg(path, opMethod, i) |
404 | return Path(path), true |
405 | } |
406 | } |
407 | |
408 | panic(fmt.Sprintf("couldn't find method %s on type %s", meth, named)) |
409 | } |
410 | |
411 | // find finds obj within type T, returning the path to it, or nil if not found. |
412 | // |
413 | // The seen map is used to short circuit cycles through type parameters. If |
414 | // nil, it will be allocated as necessary. |
415 | func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte { |
416 | switch T := T.(type) { |
417 | case *types.Basic, *types.Named: |
418 | // Named types belonging to pkg were handled already, |
419 | // so T must belong to another package. No path. |
420 | return nil |
421 | case *types.Pointer: |
422 | return find(obj, T.Elem(), append(path, opElem), seen) |
423 | case *types.Slice: |
424 | return find(obj, T.Elem(), append(path, opElem), seen) |
425 | case *types.Array: |
426 | return find(obj, T.Elem(), append(path, opElem), seen) |
427 | case *types.Chan: |
428 | return find(obj, T.Elem(), append(path, opElem), seen) |
429 | case *types.Map: |
430 | if r := find(obj, T.Key(), append(path, opKey), seen); r != nil { |
431 | return r |
432 | } |
433 | return find(obj, T.Elem(), append(path, opElem), seen) |
434 | case *types.Signature: |
435 | if r := findTypeParam(obj, typeparams.ForSignature(T), path, seen); r != nil { |
436 | return r |
437 | } |
438 | if r := find(obj, T.Params(), append(path, opParams), seen); r != nil { |
439 | return r |
440 | } |
441 | return find(obj, T.Results(), append(path, opResults), seen) |
442 | case *types.Struct: |
443 | for i := 0; i < T.NumFields(); i++ { |
444 | fld := T.Field(i) |
445 | path2 := appendOpArg(path, opField, i) |
446 | if fld == obj { |
447 | return path2 // found field var |
448 | } |
449 | if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil { |
450 | return r |
451 | } |
452 | } |
453 | return nil |
454 | case *types.Tuple: |
455 | for i := 0; i < T.Len(); i++ { |
456 | v := T.At(i) |
457 | path2 := appendOpArg(path, opAt, i) |
458 | if v == obj { |
459 | return path2 // found param/result var |
460 | } |
461 | if r := find(obj, v.Type(), append(path2, opType), seen); r != nil { |
462 | return r |
463 | } |
464 | } |
465 | return nil |
466 | case *types.Interface: |
467 | for i := 0; i < T.NumMethods(); i++ { |
468 | m := T.Method(i) |
469 | path2 := appendOpArg(path, opMethod, i) |
470 | if m == obj { |
471 | return path2 // found interface method |
472 | } |
473 | if r := find(obj, m.Type(), append(path2, opType), seen); r != nil { |
474 | return r |
475 | } |
476 | } |
477 | return nil |
478 | case *typeparams.TypeParam: |
479 | name := T.Obj() |
480 | if name == obj { |
481 | return append(path, opObj) |
482 | } |
483 | if seen[name] { |
484 | return nil |
485 | } |
486 | if seen == nil { |
487 | seen = make(map[*types.TypeName]bool) |
488 | } |
489 | seen[name] = true |
490 | if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil { |
491 | return r |
492 | } |
493 | return nil |
494 | } |
495 | panic(T) |
496 | } |
497 | |
498 | func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte { |
499 | for i := 0; i < list.Len(); i++ { |
500 | tparam := list.At(i) |
501 | path2 := appendOpArg(path, opTypeParam, i) |
502 | if r := find(obj, tparam, path2, seen); r != nil { |
503 | return r |
504 | } |
505 | } |
506 | return nil |
507 | } |
508 | |
509 | // Object returns the object denoted by path p within the package pkg. |
510 | func Object(pkg *types.Package, p Path) (types.Object, error) { |
511 | if p == "" { |
512 | return nil, fmt.Errorf("empty path") |
513 | } |
514 | |
515 | pathstr := string(p) |
516 | var pkgobj, suffix string |
517 | if dot := strings.IndexByte(pathstr, opType); dot < 0 { |
518 | pkgobj = pathstr |
519 | } else { |
520 | pkgobj = pathstr[:dot] |
521 | suffix = pathstr[dot:] // suffix starts with "." |
522 | } |
523 | |
524 | obj := pkg.Scope().Lookup(pkgobj) |
525 | if obj == nil { |
526 | return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj) |
527 | } |
528 | |
529 | // abstraction of *types.{Pointer,Slice,Array,Chan,Map} |
530 | type hasElem interface { |
531 | Elem() types.Type |
532 | } |
533 | // abstraction of *types.{Named,Signature} |
534 | type hasTypeParams interface { |
535 | TypeParams() *typeparams.TypeParamList |
536 | } |
537 | // abstraction of *types.{Named,TypeParam} |
538 | type hasObj interface { |
539 | Obj() *types.TypeName |
540 | } |
541 | |
542 | // The loop state is the pair (t, obj), |
543 | // exactly one of which is non-nil, initially obj. |
544 | // All suffixes start with '.' (the only object->type operation), |
545 | // followed by optional type->type operations, |
546 | // then a type->object operation. |
547 | // The cycle then repeats. |
548 | var t types.Type |
549 | for suffix != "" { |
550 | code := suffix[0] |
551 | suffix = suffix[1:] |
552 | |
553 | // Codes [AFM] have an integer operand. |
554 | var index int |
555 | switch code { |
556 | case opAt, opField, opMethod, opTypeParam: |
557 | rest := strings.TrimLeft(suffix, "0123456789") |
558 | numerals := suffix[:len(suffix)-len(rest)] |
559 | suffix = rest |
560 | i, err := strconv.Atoi(numerals) |
561 | if err != nil { |
562 | return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code) |
563 | } |
564 | index = int(i) |
565 | case opObj: |
566 | // no operand |
567 | default: |
568 | // The suffix must end with a type->object operation. |
569 | if suffix == "" { |
570 | return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code) |
571 | } |
572 | } |
573 | |
574 | if code == opType { |
575 | if t != nil { |
576 | return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType) |
577 | } |
578 | t = obj.Type() |
579 | obj = nil |
580 | continue |
581 | } |
582 | |
583 | if t == nil { |
584 | return nil, fmt.Errorf("invalid path: code %q in object context", code) |
585 | } |
586 | |
587 | // Inv: t != nil, obj == nil |
588 | |
589 | switch code { |
590 | case opElem: |
591 | hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map |
592 | if !ok { |
593 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t) |
594 | } |
595 | t = hasElem.Elem() |
596 | |
597 | case opKey: |
598 | mapType, ok := t.(*types.Map) |
599 | if !ok { |
600 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t) |
601 | } |
602 | t = mapType.Key() |
603 | |
604 | case opParams: |
605 | sig, ok := t.(*types.Signature) |
606 | if !ok { |
607 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) |
608 | } |
609 | t = sig.Params() |
610 | |
611 | case opResults: |
612 | sig, ok := t.(*types.Signature) |
613 | if !ok { |
614 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) |
615 | } |
616 | t = sig.Results() |
617 | |
618 | case opUnderlying: |
619 | named, ok := t.(*types.Named) |
620 | if !ok { |
621 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t) |
622 | } |
623 | t = named.Underlying() |
624 | |
625 | case opTypeParam: |
626 | hasTypeParams, ok := t.(hasTypeParams) // Named, Signature |
627 | if !ok { |
628 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t) |
629 | } |
630 | tparams := hasTypeParams.TypeParams() |
631 | if n := tparams.Len(); index >= n { |
632 | return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) |
633 | } |
634 | t = tparams.At(index) |
635 | |
636 | case opConstraint: |
637 | tparam, ok := t.(*typeparams.TypeParam) |
638 | if !ok { |
639 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t) |
640 | } |
641 | t = tparam.Constraint() |
642 | |
643 | case opAt: |
644 | tuple, ok := t.(*types.Tuple) |
645 | if !ok { |
646 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t) |
647 | } |
648 | if n := tuple.Len(); index >= n { |
649 | return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) |
650 | } |
651 | obj = tuple.At(index) |
652 | t = nil |
653 | |
654 | case opField: |
655 | structType, ok := t.(*types.Struct) |
656 | if !ok { |
657 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t) |
658 | } |
659 | if n := structType.NumFields(); index >= n { |
660 | return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n) |
661 | } |
662 | obj = structType.Field(index) |
663 | t = nil |
664 | |
665 | case opMethod: |
666 | hasMethods, ok := t.(hasMethods) // Interface or Named |
667 | if !ok { |
668 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t) |
669 | } |
670 | canonical := canonicalize(hasMethods) |
671 | if n := len(canonical); index >= n { |
672 | return nil, fmt.Errorf("method index %d out of range [0-%d)", index, n) |
673 | } |
674 | obj = canonical[index] |
675 | t = nil |
676 | |
677 | case opObj: |
678 | hasObj, ok := t.(hasObj) |
679 | if !ok { |
680 | return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t) |
681 | } |
682 | obj = hasObj.Obj() |
683 | t = nil |
684 | |
685 | default: |
686 | return nil, fmt.Errorf("invalid path: unknown code %q", code) |
687 | } |
688 | } |
689 | |
690 | if obj.Pkg() != pkg { |
691 | return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj) |
692 | } |
693 | |
694 | return obj, nil // success |
695 | } |
696 | |
697 | // hasMethods is an abstraction of *types.{Interface,Named}. This is pulled up |
698 | // because it is used by methodOrdering, which is in turn used by both encoding |
699 | // and decoding. |
700 | type hasMethods interface { |
701 | Method(int) *types.Func |
702 | NumMethods() int |
703 | } |
704 | |
705 | // canonicalize returns a canonical order for the methods in a hasMethod. |
706 | func canonicalize(hm hasMethods) []*types.Func { |
707 | count := hm.NumMethods() |
708 | if count <= 0 { |
709 | return nil |
710 | } |
711 | canon := make([]*types.Func, count) |
712 | for i := 0; i < count; i++ { |
713 | canon[i] = hm.Method(i) |
714 | } |
715 | less := func(i, j int) bool { |
716 | return canon[i].Id() < canon[j].Id() |
717 | } |
718 | sort.Slice(canon, less) |
719 | return canon |
720 | } |
721 |
Members