1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
37
38
39
40
41 type Path string
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 const (
95
96 opType = '.'
97
98
99 opElem = 'E'
100 opKey = 'K'
101 opParams = 'P'
102 opResults = 'R'
103 opUnderlying = 'U'
104 opTypeParam = 'T'
105 opConstraint = 'C'
106
107
108 opAt = 'A'
109 opField = 'F'
110 opMethod = 'M'
111 opObj = 'O'
112 )
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146 func For(obj types.Object) (Path, error) {
147 pkg := obj.Pkg()
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184 if pkg == nil {
185 return "", fmt.Errorf("predeclared %s has no path", obj)
186 }
187 scope := pkg.Scope()
188
189
190 if scope.Lookup(obj.Name()) == obj {
191
192
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
200
201 switch obj := obj.(type) {
202 case *types.TypeName:
203 if _, ok := obj.Type().(*typeparams.TypeParam); !ok {
204
205
206 return "", fmt.Errorf("no path for %v", obj)
207 }
208 case *types.Const,
209 *types.Label,
210 *types.PkgName:
211 return "", fmt.Errorf("no path for %v", obj)
212
213 case *types.Var:
214
215
216
217
218
219
220
221
222 case *types.Func:
223
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
230 return path, nil
231 }
232
233 default:
234 panic(obj)
235 }
236
237
238
239
240
241
242
243 empty := make([]byte, 0, 48)
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
250 }
251
252 path := append(empty, name...)
253 path = append(path, opType)
254
255 T := o.Type()
256
257 if tname.IsAlias() {
258
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
266 return Path(r), nil
267 }
268 }
269
270 if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil {
271 return Path(r), nil
272 }
273 }
274 }
275
276
277
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
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
292 if T, ok := o.Type().(*types.Named); ok {
293 path = append(path, opType)
294
295
296
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
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
321
322
323
324
325
326
327 func concreteMethod(meth *types.Func) (Path, bool) {
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
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
389
390
391
392 return "", false
393 }
394
395
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
412
413
414
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
419
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
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
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
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
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:]
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
530 type hasElem interface {
531 Elem() types.Type
532 }
533
534 type hasTypeParams interface {
535 TypeParams() *typeparams.TypeParamList
536 }
537
538 type hasObj interface {
539 Obj() *types.TypeName
540 }
541
542
543
544
545
546
547
548 var t types.Type
549 for suffix != "" {
550 code := suffix[0]
551 suffix = suffix[1:]
552
553
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
567 default:
568
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
588
589 switch code {
590 case opElem:
591 hasElem, ok := t.(hasElem)
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)
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)
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
695 }
696
697
698
699
700 type hasMethods interface {
701 Method(int) *types.Func
702 NumMethods() int
703 }
704
705
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
View as plain text