1// Copyright 2013 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
5package pointer
6
7import (
8	"fmt"
9	"go/token"
10	"go/types"
11	"strings"
12
13	"golang.org/x/tools/go/ssa"
14)
15
16// A Label is an entity that may be pointed to by a pointer, map,
17// channel, 'func', slice or interface.
18//
19// Labels include:
20//      - functions
21//      - globals
22//      - tagged objects, representing interfaces and reflect.Values
23//      - arrays created by conversions (e.g. []byte("foo"), []byte(s))
24//      - stack- and heap-allocated variables (including composite literals)
25//      - channels, maps and arrays created by make()
26//      - intrinsic or reflective operations that allocate (e.g. append, reflect.New)
27//      - intrinsic objects, e.g. the initial array behind os.Args.
28//      - and their subelements, e.g. "alloc.y[*].z"
29//
30// Labels are so varied that they defy good generalizations;
31// some have no value, no callgraph node, or no position.
32// Many objects have types that are inexpressible in Go:
33// maps, channels, functions, tagged objects.
34//
35// At most one of Value() or ReflectType() may return non-nil.
36//
37type Label struct {
38	obj        *object    // the addressable memory location containing this label
39	subelement *fieldInfo // subelement path within obj, e.g. ".a.b[*].c"
40}
41
42// Value returns the ssa.Value that allocated this label's object, if any.
43func (l Label) Value() ssa.Value {
44	val, _ := l.obj.data.(ssa.Value)
45	return val
46}
47
48// ReflectType returns the type represented by this label if it is an
49// reflect.rtype instance object or *reflect.rtype-tagged object.
50//
51func (l Label) ReflectType() types.Type {
52	rtype, _ := l.obj.data.(types.Type)
53	return rtype
54}
55
56// Path returns the path to the subelement of the object containing
57// this label.  For example, ".x[*].y".
58//
59func (l Label) Path() string {
60	return l.subelement.path()
61}
62
63// Pos returns the position of this label, if known, zero otherwise.
64func (l Label) Pos() token.Pos {
65	switch data := l.obj.data.(type) {
66	case ssa.Value:
67		return data.Pos()
68	case types.Type:
69		if nt, ok := deref(data).(*types.Named); ok {
70			return nt.Obj().Pos()
71		}
72	}
73	if cgn := l.obj.cgn; cgn != nil {
74		return cgn.fn.Pos()
75	}
76	return token.NoPos
77}
78
79// String returns the printed form of this label.
80//
81// Examples:                                    Object type:
82//      x                                       (a variable)
83//      (sync.Mutex).Lock                       (a function)
84//      convert                                 (array created by conversion)
85//      makemap                                 (map allocated via make)
86//      makechan                                (channel allocated via make)
87//      makeinterface                           (tagged object allocated by makeinterface)
88//      <alloc in reflect.Zero>                 (allocation in instrinsic)
89//      sync.Mutex                              (a reflect.rtype instance)
90//      <command-line arguments>                (an intrinsic object)
91//
92// Labels within compound objects have subelement paths:
93//      x.y[*].z                                (a struct variable, x)
94//      append.y[*].z                           (array allocated by append)
95//      makeslice.y[*].z                        (array allocated via make)
96//
97// TODO(adonovan): expose func LabelString(*types.Package, Label).
98//
99func (l Label) String() string {
100	var s string
101	switch v := l.obj.data.(type) {
102	case types.Type:
103		return v.String()
104
105	case string:
106		s = v // an intrinsic object (e.g. os.Args[*])
107
108	case nil:
109		if l.obj.cgn != nil {
110			// allocation by intrinsic or reflective operation
111			s = fmt.Sprintf("<alloc in %s>", l.obj.cgn.fn)
112		} else {
113			s = "<unknown>" // should be unreachable
114		}
115
116	case *ssa.Function:
117		s = v.String()
118
119	case *ssa.Global:
120		s = v.String()
121
122	case *ssa.Const:
123		s = v.Name()
124
125	case *ssa.Alloc:
126		s = v.Comment
127		if s == "" {
128			s = "alloc"
129		}
130
131	case *ssa.Call:
132		// Currently only calls to append can allocate objects.
133		if v.Call.Value.(*ssa.Builtin).Object().Name() != "append" {
134			panic("unhandled *ssa.Call label: " + v.Name())
135		}
136		s = "append"
137
138	case *ssa.MakeMap, *ssa.MakeChan, *ssa.MakeSlice, *ssa.Convert:
139		s = strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa."))
140
141	case *ssa.MakeInterface:
142		// MakeInterface is usually implicit in Go source (so
143		// Pos()==0), and tagged objects may be allocated
144		// synthetically (so no *MakeInterface data).
145		s = "makeinterface:" + v.X.Type().String()
146
147	default:
148		panic(fmt.Sprintf("unhandled object data type: %T", v))
149	}
150
151	return s + l.subelement.path()
152}
153