GoPLS Viewer

Home|gopls/go/ssa/util.go
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 ssa
6
7// This file defines a number of miscellaneous utility functions.
8
9import (
10    "fmt"
11    "go/ast"
12    "go/token"
13    "go/types"
14    "io"
15    "os"
16    "sync"
17
18    "golang.org/x/tools/go/ast/astutil"
19    "golang.org/x/tools/go/types/typeutil"
20    "golang.org/x/tools/internal/typeparams"
21)
22
23//// Sanity checking utilities
24
25// assert panics with the mesage msg if p is false.
26// Avoid combining with expensive string formatting.
27func assert(p boolmsg string) {
28    if !p {
29        panic(msg)
30    }
31}
32
33//// AST utilities
34
35func unparen(e ast.Exprast.Expr { return astutil.Unparen(e) }
36
37// isBlankIdent returns true iff e is an Ident with name "_".
38// They have no associated types.Object, and thus no type.
39func isBlankIdent(e ast.Exprbool {
40    idok := e.(*ast.Ident)
41    return ok && id.Name == "_"
42}
43
44//// Type utilities.  Some of these belong in go/types.
45
46// isPointer returns true for types whose underlying type is a pointer.
47func isPointer(typ types.Typebool {
48    _ok := typ.Underlying().(*types.Pointer)
49    return ok
50}
51
52// isNonTypeParamInterface reports whether t is an interface type but not a type parameter.
53func isNonTypeParamInterface(t types.Typebool {
54    return !typeparams.IsTypeParam(t) && types.IsInterface(t)
55}
56
57// isBasic reports whether t is a basic type.
58func isBasic(t types.Typebool {
59    _ok := t.(*types.Basic)
60    return ok
61}
62
63// isString reports whether t is exactly a string type.
64func isString(t types.Typebool {
65    return isBasic(t) && t.(*types.Basic).Info()&types.IsString != 0
66}
67
68// isByteSlice reports whether t is []byte.
69func isByteSlice(t types.Typebool {
70    if bok := t.(*types.Slice); ok {
71        e_ := b.Elem().(*types.Basic)
72        return e != nil && e.Kind() == types.Byte
73    }
74    return false
75}
76
77// isRuneSlice reports whether t is []rune.
78func isRuneSlice(t types.Typebool {
79    if bok := t.(*types.Slice); ok {
80        e_ := b.Elem().(*types.Basic)
81        return e != nil && e.Kind() == types.Rune
82    }
83    return false
84}
85
86// isBasicConvType returns true when a type set can be
87// one side of a Convert operation. This is when:
88// - All are basic, []byte, or []rune.
89// - At least 1 is basic.
90// - At most 1 is []byte or []rune.
91func isBasicConvTypes(tset termListbool {
92    basics := 0
93    all := underIs(tset, func(t types.Typebool {
94        if isBasic(t) {
95            basics++
96            return true
97        }
98        return isByteSlice(t) || isRuneSlice(t)
99    })
100    return all && basics >= 1 && tset.Len()-basics <= 1
101}
102
103// deref returns a pointer's element type; otherwise it returns typ.
104func deref(typ types.Typetypes.Type {
105    if pok := typ.Underlying().(*types.Pointer); ok {
106        return p.Elem()
107    }
108    return typ
109}
110
111// recvType returns the receiver type of method obj.
112func recvType(obj *types.Functypes.Type {
113    return obj.Type().(*types.Signature).Recv().Type()
114}
115
116// isUntyped returns true for types that are untyped.
117func isUntyped(typ types.Typebool {
118    bok := typ.(*types.Basic)
119    return ok && b.Info()&types.IsUntyped != 0
120}
121
122// logStack prints the formatted "start" message to stderr and
123// returns a closure that prints the corresponding "end" message.
124// Call using 'defer logStack(...)()' to show builder stack on panic.
125// Don't forget trailing parens!
126func logStack(format stringargs ...interface{}) func() {
127    msg := fmt.Sprintf(formatargs...)
128    io.WriteString(os.Stderrmsg)
129    io.WriteString(os.Stderr"\n")
130    return func() {
131        io.WriteString(os.Stderrmsg)
132        io.WriteString(os.Stderr" end\n")
133    }
134}
135
136// newVar creates a 'var' for use in a types.Tuple.
137func newVar(name stringtyp types.Type) *types.Var {
138    return types.NewParam(token.NoPosnilnametyp)
139}
140
141// anonVar creates an anonymous 'var' for use in a types.Tuple.
142func anonVar(typ types.Type) *types.Var {
143    return newVar(""typ)
144}
145
146var lenResults = types.NewTuple(anonVar(tInt))
147
148// makeLen returns the len builtin specialized to type func(T)int.
149func makeLen(T types.Type) *Builtin {
150    lenParams := types.NewTuple(anonVar(T))
151    return &Builtin{
152        name"len",
153        sig:  types.NewSignature(nillenParamslenResultsfalse),
154    }
155}
156
157// nonbasicTypes returns a list containing all of the types T in ts that are non-basic.
158func nonbasicTypes(ts []types.Type) []types.Type {
159    if len(ts) == 0 {
160        return nil
161    }
162    added := make(map[types.Type]bool// additionally filter duplicates
163    var filtered []types.Type
164    for _T := range ts {
165        if !isBasic(T) {
166            if !added[T] {
167                added[T] = true
168                filtered = append(filteredT)
169            }
170        }
171    }
172    return filtered
173}
174
175// receiverTypeArgs returns the type arguments to a function's reciever.
176// Returns an empty list if obj does not have a reciever or its reciever does not have type arguments.
177func receiverTypeArgs(obj *types.Func) []types.Type {
178    rtype := recvType(obj)
179    if rtype == nil {
180        return nil
181    }
182    if isPointer(rtype) {
183        rtype = rtype.(*types.Pointer).Elem()
184    }
185    namedok := rtype.(*types.Named)
186    if !ok {
187        return nil
188    }
189    ts := typeparams.NamedTypeArgs(named)
190    if ts.Len() == 0 {
191        return nil
192    }
193    targs := make([]types.Typets.Len())
194    for i := 0i < ts.Len(); i++ {
195        targs[i] = ts.At(i)
196    }
197    return targs
198}
199
200// recvAsFirstArg takes a method signature and returns a function
201// signature with receiver as the first parameter.
202func recvAsFirstArg(sig *types.Signature) *types.Signature {
203    params := make([]*types.Var01+sig.Params().Len())
204    params = append(paramssig.Recv())
205    for i := 0i < sig.Params().Len(); i++ {
206        params = append(paramssig.Params().At(i))
207    }
208    return typeparams.NewSignatureType(nilnilniltypes.NewTuple(params...), sig.Results(), sig.Variadic())
209}
210
211// instance returns whether an expression is a simple or qualified identifier
212// that is a generic instantiation.
213func instance(info *types.Infoexpr ast.Exprbool {
214    // Compare the logic here against go/types.instantiatedIdent,
215    // which also handles  *IndexExpr and *IndexListExpr.
216    var id *ast.Ident
217    switch x := expr.(type) {
218    case *ast.Ident:
219        id = x
220    case *ast.SelectorExpr:
221        id = x.Sel
222    default:
223        return false
224    }
225    _ok := typeparams.GetInstances(info)[id]
226    return ok
227}
228
229// instanceArgs returns the Instance[id].TypeArgs as a slice.
230func instanceArgs(info *types.Infoid *ast.Ident) []types.Type {
231    targList := typeparams.GetInstances(info)[id].TypeArgs
232    if targList == nil {
233        return nil
234    }
235
236    targs := make([]types.TypetargList.Len())
237    for in := 0targList.Len(); i < ni++ {
238        targs[i] = targList.At(i)
239    }
240    return targs
241}
242
243// Mapping of a type T to a canonical instance C s.t. types.Indentical(T, C).
244// Thread-safe.
245type canonizer struct {
246    mu    sync.Mutex
247    types typeutil.Map // map from type to a canonical instance
248    lists typeListMap  // map from a list of types to a canonical instance
249}
250
251func newCanonizer() *canonizer {
252    c := &canonizer{}
253    h := typeutil.MakeHasher()
254    c.types.SetHasher(h)
255    c.lists.hasher = h
256    return c
257}
258
259// List returns a canonical representative of a list of types.
260// Representative of the empty list is nil.
261func (c *canonizerList(ts []types.Type) *typeList {
262    if len(ts) == 0 {
263        return nil
264    }
265
266    c.mu.Lock()
267    defer c.mu.Unlock()
268    return c.lists.rep(ts)
269}
270
271// Type returns a canonical representative of type T.
272func (c *canonizerType(T types.Typetypes.Type {
273    c.mu.Lock()
274    defer c.mu.Unlock()
275
276    if r := c.types.At(T); r != nil {
277        return r.(types.Type)
278    }
279    c.types.Set(TT)
280    return T
281}
282
283// A type for representating an canonized list of types.
284type typeList []types.Type
285
286func (l *typeListidentical(ts []types.Typebool {
287    if l == nil {
288        return len(ts) == 0
289    }
290    n := len(*l)
291    if len(ts) != n {
292        return false
293    }
294    for ileft := range *l {
295        right := ts[i]
296        if !types.Identical(leftright) {
297            return false
298        }
299    }
300    return true
301}
302
303type typeListMap struct {
304    hasher  typeutil.Hasher
305    buckets map[uint32][]*typeList
306}
307
308// rep returns a canonical representative of a slice of types.
309func (m *typeListMaprep(ts []types.Type) *typeList {
310    if m == nil || len(ts) == 0 {
311        return nil
312    }
313
314    if m.buckets == nil {
315        m.buckets = make(map[uint32][]*typeList)
316    }
317
318    h := m.hash(ts)
319    bucket := m.buckets[h]
320    for _l := range bucket {
321        if l.identical(ts) {
322            return l
323        }
324    }
325
326    // not present. create a representative.
327    cp := make(typeListlen(ts))
328    copy(cpts)
329    rep := &cp
330
331    m.buckets[h] = append(bucketrep)
332    return rep
333}
334
335func (m *typeListMaphash(ts []types.Typeuint32 {
336    if m == nil {
337        return 0
338    }
339    // Some smallish prime far away from typeutil.Hash.
340    n := len(ts)
341    h := uint32(13619) + 2*uint32(n)
342    for i := 0i < ni++ {
343        h += 3 * m.hasher.Hash(ts[i])
344    }
345    return h
346}
347
348// instantiateMethod instantiates m with targs and returns a canonical representative for this method.
349func (canon *canonizerinstantiateMethod(m *types.Functargs []types.Typectxt *typeparams.Context) *types.Func {
350    recv := recvType(m)
351    if pok := recv.(*types.Pointer); ok {
352        recv = p.Elem()
353    }
354    named := recv.(*types.Named)
355    insterr := typeparams.Instantiate(ctxttypeparams.NamedTypeOrigin(named), targsfalse)
356    if err != nil {
357        panic(err)
358    }
359    rep := canon.Type(inst)
360    obj__ := types.LookupFieldOrMethod(reptruem.Pkg(), m.Name())
361    return obj.(*types.Func)
362}
363
MembersX
isRuneSlice
recvType.obj
nonbasicTypes
instanceArgs.info
newCanonizer
canonizer.instantiateMethod.targs
canonizer.Type
typeList
isBlankIdent.e
isBasicConvTypes.all
typeListMap
canonizer.instantiateMethod.err
canonizer
canonizer.mu
canonizer.instantiateMethod._
typeList.identical.RangeStmt_7605.i
typeListMap.buckets
newVar.name
recvAsFirstArg
instance.expr
instanceArgs.targs
canonizer.Type.T
typeListMap.rep
isPointer.typ
isRuneSlice.t
typeListMap.rep.RangeStmt_8088.l
canonizer.instantiateMethod.m
assert.p
isUntyped.typ
instanceArgs
typeList.identical
isNonTypeParamInterface
isString.t
instanceArgs.i
newCanonizer.h
typeListMap.rep.cp
typeListMap.hash
isBasicConvTypes.tset
logStack
isString
receiverTypeArgs.obj
canonizer.Type.r
canonizer.instantiateMethod.ctxt
isBlankIdent
isByteSlice
newVar
receiverTypeArgs.rtype
recvAsFirstArg.i
canonizer.List.c
anonVar.typ
makeLen.T
canonizer.types
assert
typeListMap.hash.m
typeList.identical.ts
receiverTypeArgs.targs
instance.id
instanceArgs.n
typeListMap.hasher
typeListMap.hash.n
nonbasicTypes.added
typeListMap.rep.m
makeLen.lenParams
typeList.identical.RangeStmt_7605.left
unparen
isBasic.t
anonVar
instance.info
canonizer.instantiateMethod.obj
isByteSlice.t
isBasicConvTypes.basics
instance
canonizer.Type.c
typeList.identical.l
typeListMap.hash.i
assert.msg
isPointer
newVar.typ
makeLen
canonizer.List.ts
deref
logStack.format
logStack.args
nonbasicTypes.ts
typeListMap.rep.ts
canonizer.instantiateMethod
unparen.e
isNonTypeParamInterface.t
typeListMap.rep.h
typeListMap.hash.ts
logStack.msg
nonbasicTypes.filtered
receiverTypeArgs
instanceArgs.id
deref.typ
receiverTypeArgs.ts
canonizer.instantiateMethod.canon
canonizer.instantiateMethod.recv
canonizer.instantiateMethod.inst
recvType
recvAsFirstArg.sig
typeList.identical.n
typeListMap.rep.rep
canonizer.instantiateMethod.rep
recvAsFirstArg.params
isBasic
isBasicConvTypes
instanceArgs.targList
newCanonizer.c
nonbasicTypes.RangeStmt_4405.T
canonizer.List
isUntyped
receiverTypeArgs.i
canonizer.lists
Members
X