--- /dev/null
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pointer
+
+import (
+ "fmt"
+ "go/token"
+ "go/types"
+ "strings"
+
+ "golang.org/x/tools/go/ssa"
+)
+
+// A Label is an entity that may be pointed to by a pointer, map,
+// channel, 'func', slice or interface.
+//
+// Labels include:
+// - functions
+// - globals
+// - tagged objects, representing interfaces and reflect.Values
+// - arrays created by conversions (e.g. []byte("foo"), []byte(s))
+// - stack- and heap-allocated variables (including composite literals)
+// - channels, maps and arrays created by make()
+// - intrinsic or reflective operations that allocate (e.g. append, reflect.New)
+// - intrinsic objects, e.g. the initial array behind os.Args.
+// - and their subelements, e.g. "alloc.y[*].z"
+//
+// Labels are so varied that they defy good generalizations;
+// some have no value, no callgraph node, or no position.
+// Many objects have types that are inexpressible in Go:
+// maps, channels, functions, tagged objects.
+//
+// At most one of Value() or ReflectType() may return non-nil.
+//
+type Label struct {
+ obj *object // the addressable memory location containing this label
+ subelement *fieldInfo // subelement path within obj, e.g. ".a.b[*].c"
+}
+
+// Value returns the ssa.Value that allocated this label's object, if any.
+func (l Label) Value() ssa.Value {
+ val, _ := l.obj.data.(ssa.Value)
+ return val
+}
+
+// ReflectType returns the type represented by this label if it is an
+// reflect.rtype instance object or *reflect.rtype-tagged object.
+//
+func (l Label) ReflectType() types.Type {
+ rtype, _ := l.obj.data.(types.Type)
+ return rtype
+}
+
+// Path returns the path to the subelement of the object containing
+// this label. For example, ".x[*].y".
+//
+func (l Label) Path() string {
+ return l.subelement.path()
+}
+
+// Pos returns the position of this label, if known, zero otherwise.
+func (l Label) Pos() token.Pos {
+ switch data := l.obj.data.(type) {
+ case ssa.Value:
+ return data.Pos()
+ case types.Type:
+ if nt, ok := deref(data).(*types.Named); ok {
+ return nt.Obj().Pos()
+ }
+ }
+ if cgn := l.obj.cgn; cgn != nil {
+ return cgn.fn.Pos()
+ }
+ return token.NoPos
+}
+
+// String returns the printed form of this label.
+//
+// Examples: Object type:
+// x (a variable)
+// (sync.Mutex).Lock (a function)
+// convert (array created by conversion)
+// makemap (map allocated via make)
+// makechan (channel allocated via make)
+// makeinterface (tagged object allocated by makeinterface)
+// <alloc in reflect.Zero> (allocation in instrinsic)
+// sync.Mutex (a reflect.rtype instance)
+// <command-line arguments> (an intrinsic object)
+//
+// Labels within compound objects have subelement paths:
+// x.y[*].z (a struct variable, x)
+// append.y[*].z (array allocated by append)
+// makeslice.y[*].z (array allocated via make)
+//
+// TODO(adonovan): expose func LabelString(*types.Package, Label).
+//
+func (l Label) String() string {
+ var s string
+ switch v := l.obj.data.(type) {
+ case types.Type:
+ return v.String()
+
+ case string:
+ s = v // an intrinsic object (e.g. os.Args[*])
+
+ case nil:
+ if l.obj.cgn != nil {
+ // allocation by intrinsic or reflective operation
+ s = fmt.Sprintf("<alloc in %s>", l.obj.cgn.fn)
+ } else {
+ s = "<unknown>" // should be unreachable
+ }
+
+ case *ssa.Function:
+ s = v.String()
+
+ case *ssa.Global:
+ s = v.String()
+
+ case *ssa.Const:
+ s = v.Name()
+
+ case *ssa.Alloc:
+ s = v.Comment
+ if s == "" {
+ s = "alloc"
+ }
+
+ case *ssa.Call:
+ // Currently only calls to append can allocate objects.
+ if v.Call.Value.(*ssa.Builtin).Object().Name() != "append" {
+ panic("unhandled *ssa.Call label: " + v.Name())
+ }
+ s = "append"
+
+ case *ssa.MakeMap, *ssa.MakeChan, *ssa.MakeSlice, *ssa.Convert:
+ s = strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa."))
+
+ case *ssa.MakeInterface:
+ // MakeInterface is usually implicit in Go source (so
+ // Pos()==0), and tagged objects may be allocated
+ // synthetically (so no *MakeInterface data).
+ s = "makeinterface:" + v.X.Type().String()
+
+ default:
+ panic(fmt.Sprintf("unhandled object data type: %T", v))
+ }
+
+ return s + l.subelement.path()
+}