GoPLS Viewer

Home|gopls/go/types/objectpath/objectpath.go
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.
24package objectpath
25
26import (
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.
41type 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.
94const (
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.
146func For(obj types.Object) (Patherror) {
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 pathok := concreteMethod(obj); ok {
229            // Fast path for concrete methods that avoids looping over scope.
230            return pathnil
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([]byte048// initial space
244    names := scope.Names()
245    for _name := range names {
246        o := scope.Lookup(name)
247        tnameok := o.(*types.TypeName)
248        if !ok {
249            continue // handle non-types in second pass
250        }
251
252        path := append(emptyname...)
253        path = append(pathopType)
254
255        T := o.Type()
256
257        if tname.IsAlias() {
258            // type alias
259            if r := find(objTpathnil); r != nil {
260                return Path(r), nil
261            }
262        } else {
263            if named_ := T.(*types.Named); named != nil {
264                if r := findTypeParam(objtypeparams.ForNamed(named), pathnil); r != nil {
265                    // generic named type
266                    return Path(r), nil
267                }
268            }
269            // defined (named) type
270            if r := find(objT.Underlying(), append(pathopUnderlying), 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(emptyname...)
281        if _ok := o.(*types.TypeName); !ok {
282            if o.Exported() {
283                // exported non-type (const, var, func)
284                if r := find(objo.Type(), append(pathopType), nil); r != nil {
285                    return Path(r), nil
286                }
287            }
288            continue
289        }
290
291        // Inspect declared methods of defined types.
292        if Tok := o.Type().(*types.Named); ok {
293            path = append(pathopType)
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 := 0i < len(canonical); i++ {
299                m := canonical[i]
300                path2 := appendOpArg(pathopMethodi)
301                if m == obj {
302                    return Path(path2), nil // found declared method
303                }
304                if r := find(objm.Type(), append(path2opType), 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"objpkg.Path())
312}
313
314func appendOpArg(path []byteop bytearg int) []byte {
315    path = append(pathop)
316    path = strconv.AppendInt(pathint64(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.
327func concreteMethod(meth *types.Func) (Pathbool) {
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 ptrok := recvT.(*types.Pointer); ok {
379        recvT = ptr.Elem()
380    }
381
382    namedok := 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([]byte0len(name)+8)
398    path = append(pathname...)
399    path = append(pathopType)
400    canonical := canonicalize(named)
401    for im := range canonical {
402        if m == meth {
403            path = appendOpArg(pathopMethodi)
404            return Path(path), true
405        }
406    }
407
408    panic(fmt.Sprintf("couldn't find method %s on type %s"methnamed))
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.
415func find(obj types.ObjectT types.Typepath []byteseen 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(objT.Elem(), append(pathopElem), seen)
423    case *types.Slice:
424        return find(objT.Elem(), append(pathopElem), seen)
425    case *types.Array:
426        return find(objT.Elem(), append(pathopElem), seen)
427    case *types.Chan:
428        return find(objT.Elem(), append(pathopElem), seen)
429    case *types.Map:
430        if r := find(objT.Key(), append(pathopKey), seen); r != nil {
431            return r
432        }
433        return find(objT.Elem(), append(pathopElem), seen)
434    case *types.Signature:
435        if r := findTypeParam(objtypeparams.ForSignature(T), pathseen); r != nil {
436            return r
437        }
438        if r := find(objT.Params(), append(pathopParams), seen); r != nil {
439            return r
440        }
441        return find(objT.Results(), append(pathopResults), seen)
442    case *types.Struct:
443        for i := 0i < T.NumFields(); i++ {
444            fld := T.Field(i)
445            path2 := appendOpArg(pathopFieldi)
446            if fld == obj {
447                return path2 // found field var
448            }
449            if r := find(objfld.Type(), append(path2opType), seen); r != nil {
450                return r
451            }
452        }
453        return nil
454    case *types.Tuple:
455        for i := 0i < T.Len(); i++ {
456            v := T.At(i)
457            path2 := appendOpArg(pathopAti)
458            if v == obj {
459                return path2 // found param/result var
460            }
461            if r := find(objv.Type(), append(path2opType), seen); r != nil {
462                return r
463            }
464        }
465        return nil
466    case *types.Interface:
467        for i := 0i < T.NumMethods(); i++ {
468            m := T.Method(i)
469            path2 := appendOpArg(pathopMethodi)
470            if m == obj {
471                return path2 // found interface method
472            }
473            if r := find(objm.Type(), append(path2opType), 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(pathopObj)
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(objT.Constraint(), append(pathopConstraint), seen); r != nil {
491            return r
492        }
493        return nil
494    }
495    panic(T)
496}
497
498func findTypeParam(obj types.Objectlist *typeparams.TypeParamListpath []byteseen map[*types.TypeName]bool) []byte {
499    for i := 0i < list.Len(); i++ {
500        tparam := list.At(i)
501        path2 := appendOpArg(pathopTypeParami)
502        if r := find(objtparampath2seen); 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.
510func Object(pkg *types.Packagep Path) (types.Objecterror) {
511    if p == "" {
512        return nilfmt.Errorf("empty path")
513    }
514
515    pathstr := string(p)
516    var pkgobjsuffix string
517    if dot := strings.IndexByte(pathstropType); 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 nilfmt.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 opAtopFieldopMethodopTypeParam:
557            rest := strings.TrimLeft(suffix"0123456789")
558            numerals := suffix[:len(suffix)-len(rest)]
559            suffix = rest
560            ierr := strconv.Atoi(numerals)
561            if err != nil {
562                return nilfmt.Errorf("invalid path: bad numeric operand %q for code %q"numeralscode)
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 nilfmt.Errorf("invalid path: ends with %q, want [AFMO]"code)
571            }
572        }
573
574        if code == opType {
575            if t != nil {
576                return nilfmt.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 nilfmt.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            hasElemok := t.(hasElem// Pointer, Slice, Array, Chan, Map
592            if !ok {
593                return nilfmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)"codett)
594            }
595            t = hasElem.Elem()
596
597        case opKey:
598            mapTypeok := t.(*types.Map)
599            if !ok {
600                return nilfmt.Errorf("cannot apply %q to %s (got %T, want map)"codett)
601            }
602            t = mapType.Key()
603
604        case opParams:
605            sigok := t.(*types.Signature)
606            if !ok {
607                return nilfmt.Errorf("cannot apply %q to %s (got %T, want signature)"codett)
608            }
609            t = sig.Params()
610
611        case opResults:
612            sigok := t.(*types.Signature)
613            if !ok {
614                return nilfmt.Errorf("cannot apply %q to %s (got %T, want signature)"codett)
615            }
616            t = sig.Results()
617
618        case opUnderlying:
619            namedok := t.(*types.Named)
620            if !ok {
621                return nilfmt.Errorf("cannot apply %q to %s (got %T, want named)"codett)
622            }
623            t = named.Underlying()
624
625        case opTypeParam:
626            hasTypeParamsok := t.(hasTypeParams// Named, Signature
627            if !ok {
628                return nilfmt.Errorf("cannot apply %q to %s (got %T, want named or signature)"codett)
629            }
630            tparams := hasTypeParams.TypeParams()
631            if n := tparams.Len(); index >= n {
632                return nilfmt.Errorf("tuple index %d out of range [0-%d)"indexn)
633            }
634            t = tparams.At(index)
635
636        case opConstraint:
637            tparamok := t.(*typeparams.TypeParam)
638            if !ok {
639                return nilfmt.Errorf("cannot apply %q to %s (got %T, want type parameter)"codett)
640            }
641            t = tparam.Constraint()
642
643        case opAt:
644            tupleok := t.(*types.Tuple)
645            if !ok {
646                return nilfmt.Errorf("cannot apply %q to %s (got %T, want tuple)"codett)
647            }
648            if n := tuple.Len(); index >= n {
649                return nilfmt.Errorf("tuple index %d out of range [0-%d)"indexn)
650            }
651            obj = tuple.At(index)
652            t = nil
653
654        case opField:
655            structTypeok := t.(*types.Struct)
656            if !ok {
657                return nilfmt.Errorf("cannot apply %q to %s (got %T, want struct)"codett)
658            }
659            if n := structType.NumFields(); index >= n {
660                return nilfmt.Errorf("field index %d out of range [0-%d)"indexn)
661            }
662            obj = structType.Field(index)
663            t = nil
664
665        case opMethod:
666            hasMethodsok := t.(hasMethods// Interface or Named
667            if !ok {
668                return nilfmt.Errorf("cannot apply %q to %s (got %T, want interface or named)"codett)
669            }
670            canonical := canonicalize(hasMethods)
671            if n := len(canonical); index >= n {
672                return nilfmt.Errorf("method index %d out of range [0-%d)"indexn)
673            }
674            obj = canonical[index]
675            t = nil
676
677        case opObj:
678            hasObjok := t.(hasObj)
679            if !ok {
680                return nilfmt.Errorf("cannot apply %q to %s (got %T, want named or type param)"codett)
681            }
682            obj = hasObj.Obj()
683            t = nil
684
685        default:
686            return nilfmt.Errorf("invalid path: unknown code %q"code)
687        }
688    }
689
690    if obj.Pkg() != pkg {
691        return nilfmt.Errorf("path denotes %s, which belongs to a different package"obj)
692    }
693
694    return objnil // 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.
700type hasMethods interface {
701    Method(int) *types.Func
702    NumMethods() int
703}
704
705// canonicalize returns a canonical order for the methods in a hasMethod.
706func canonicalize(hm hasMethods) []*types.Func {
707    count := hm.NumMethods()
708    if count <= 0 {
709        return nil
710    }
711    canon := make([]*types.Funccount)
712    for i := 0i < counti++ {
713        canon[i] = hm.Method(i)
714    }
715    less := func(ij intbool {
716        return canon[i].Id() < canon[j].Id()
717    }
718    sort.Slice(canonless)
719    return canon
720}
721
MembersX
For.RangeStmt_8863.BlockStmt.path
For.RangeStmt_8863.BlockStmt.BlockStmt.BlockStmt.r
For.RangeStmt_8863.BlockStmt.BlockStmt.canonical
concreteMethod.canonical
concreteMethod.RangeStmt_13422.m
find.obj
canonicalize.i
concreteMethod.meth
findTypeParam.seen
Object.BlockStmt.BlockStmt.canonical
opField
hasMethods
opObj
For.BlockStmt.recv
find
opUnderlying
For.empty
For.RangeStmt_8863.BlockStmt.BlockStmt.BlockStmt.path2
find.BlockStmt.BlockStmt.r
findTypeParam.path
canonicalize.hm
types
Path
opTypeParam
For.BlockStmt.path
For.names
find.BlockStmt.i
Object.suffix
fmt
opConstraint
For.RangeStmt_8863.name
concreteMethod
find.seen
find.BlockStmt.BlockStmt.v
Object.p
For.obj
For.RangeStmt_8863.BlockStmt.o
appendOpArg
find.path
find.BlockStmt.name
findTypeParam.obj
findTypeParam.i
canonicalize
opType
opMethod
For.BlockStmt.ok
For.RangeStmt_8071.name
concreteMethod.name
findTypeParam.list
findTypeParam.BlockStmt.tparam
Object.t
Object.BlockStmt.index
opKey
opResults
Object.pkg
Object.hasElem
Object.BlockStmt.BlockStmt.rest
Object.BlockStmt.BlockStmt.n
strings
For.pkg
For.RangeStmt_8071.BlockStmt.o
find.T
findTypeParam.BlockStmt.path2
Object.hasTypeParams
Object.BlockStmt.BlockStmt.err
strconv
typeparams
For.scope
concreteMethod.recvT
findTypeParam
Object.obj
concreteMethod.path
opElem
opParams
For.RangeStmt_8071.BlockStmt.T
For.RangeStmt_8071.BlockStmt.BlockStmt.r
For.RangeStmt_8071.BlockStmt.BlockStmt.BlockStmt.r
For.RangeStmt_8863.BlockStmt.BlockStmt.i
appendOpArg.arg
canonicalize.canon
find.BlockStmt.r
find.BlockStmt.BlockStmt.path2
find.BlockStmt.BlockStmt.m
findTypeParam.BlockStmt.r
Object.pathstr
Object.BlockStmt.BlockStmt.tparams
canonicalize.count
Object.hasObj
sort
For
appendOpArg.path
appendOpArg.op
concreteMethod.RangeStmt_13422.i
find.BlockStmt.BlockStmt.fld
Object.dot
opAt
For.RangeStmt_8071.BlockStmt.path
Object
Object.pkgobj
Object.BlockStmt.BlockStmt.i
Members
X