golang.org/x/tools@v0.21.0/internal/refactor/inline/util.go (about) 1 // Copyright 2023 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 inline 6 7 // This file defines various common helpers. 8 9 import ( 10 "go/ast" 11 "go/constant" 12 "go/token" 13 "go/types" 14 "reflect" 15 "strings" 16 17 "golang.org/x/tools/internal/typeparams" 18 ) 19 20 func is[T any](x any) bool { 21 _, ok := x.(T) 22 return ok 23 } 24 25 // TODO(adonovan): use go1.21's slices.Clone. 26 func clone[T any](slice []T) []T { return append([]T{}, slice...) } 27 28 // TODO(adonovan): use go1.21's slices.Index. 29 func index[T comparable](slice []T, x T) int { 30 for i, elem := range slice { 31 if elem == x { 32 return i 33 } 34 } 35 return -1 36 } 37 38 func btoi(b bool) int { 39 if b { 40 return 1 41 } else { 42 return 0 43 } 44 } 45 46 func offsetOf(fset *token.FileSet, pos token.Pos) int { 47 return fset.PositionFor(pos, false).Offset 48 } 49 50 // objectKind returns an object's kind (e.g. var, func, const, typename). 51 func objectKind(obj types.Object) string { 52 return strings.TrimPrefix(strings.ToLower(reflect.TypeOf(obj).String()), "*types.") 53 } 54 55 // within reports whether pos is within the half-open interval [n.Pos, n.End). 56 func within(pos token.Pos, n ast.Node) bool { 57 return n.Pos() <= pos && pos < n.End() 58 } 59 60 // trivialConversion reports whether it is safe to omit the implicit 61 // value-to-variable conversion that occurs in argument passing or 62 // result return. The only case currently allowed is converting from 63 // untyped constant to its default type (e.g. 0 to int). 64 // 65 // The reason for this check is that converting from A to B to C may 66 // yield a different result than converting A directly to C: consider 67 // 0 to int32 to any. 68 // 69 // trivialConversion under-approximates trivial conversions, as unfortunately 70 // go/types does not record the type of an expression *before* it is implicitly 71 // converted, and therefore it cannot distinguish typed constant constant 72 // expressions from untyped constant expressions. For example, in the 73 // expression `c + 2`, where c is a uint32 constant, trivialConversion does not 74 // detect that the default type of this express is actually uint32, not untyped 75 // int. 76 // 77 // We could, of course, do better here by reverse engineering some of go/types' 78 // constant handling. That may or may not be worthwhile.. 79 func trivialConversion(fromValue constant.Value, from, to types.Type) bool { 80 if fromValue != nil { 81 var defaultType types.Type 82 switch fromValue.Kind() { 83 case constant.Bool: 84 defaultType = types.Typ[types.Bool] 85 case constant.String: 86 defaultType = types.Typ[types.String] 87 case constant.Int: 88 defaultType = types.Typ[types.Int] 89 case constant.Float: 90 defaultType = types.Typ[types.Float64] 91 case constant.Complex: 92 defaultType = types.Typ[types.Complex128] 93 default: 94 return false 95 } 96 return types.Identical(defaultType, to) 97 } 98 return types.Identical(from, to) 99 } 100 101 func checkInfoFields(info *types.Info) { 102 assert(info.Defs != nil, "types.Info.Defs is nil") 103 assert(info.Implicits != nil, "types.Info.Implicits is nil") 104 assert(info.Scopes != nil, "types.Info.Scopes is nil") 105 assert(info.Selections != nil, "types.Info.Selections is nil") 106 assert(info.Types != nil, "types.Info.Types is nil") 107 assert(info.Uses != nil, "types.Info.Uses is nil") 108 } 109 110 func funcHasTypeParams(decl *ast.FuncDecl) bool { 111 // generic function? 112 if decl.Type.TypeParams != nil { 113 return true 114 } 115 // method on generic type? 116 if decl.Recv != nil { 117 t := decl.Recv.List[0].Type 118 if u, ok := t.(*ast.StarExpr); ok { 119 t = u.X 120 } 121 return is[*ast.IndexExpr](t) || is[*ast.IndexListExpr](t) 122 } 123 return false 124 } 125 126 // intersects reports whether the maps' key sets intersect. 127 func intersects[K comparable, T1, T2 any](x map[K]T1, y map[K]T2) bool { 128 if len(x) > len(y) { 129 return intersects(y, x) 130 } 131 for k := range x { 132 if _, ok := y[k]; ok { 133 return true 134 } 135 } 136 return false 137 } 138 139 // convert returns syntax for the conversion T(x). 140 func convert(T, x ast.Expr) *ast.CallExpr { 141 // The formatter generally adds parens as needed, 142 // but before go1.22 it had a bug (#63362) for 143 // channel types that requires this workaround. 144 if ch, ok := T.(*ast.ChanType); ok && ch.Dir == ast.RECV { 145 T = &ast.ParenExpr{X: T} 146 } 147 return &ast.CallExpr{ 148 Fun: T, 149 Args: []ast.Expr{x}, 150 } 151 } 152 153 // isPointer reports whether t's core type is a pointer. 154 func isPointer(t types.Type) bool { 155 return is[*types.Pointer](typeparams.CoreType(t)) 156 } 157 158 // indirectSelection is like seln.Indirect() without bug #8353. 159 func indirectSelection(seln *types.Selection) bool { 160 // Work around bug #8353 in Selection.Indirect when Kind=MethodVal. 161 if seln.Kind() == types.MethodVal { 162 tArg, indirect := effectiveReceiver(seln) 163 if indirect { 164 return true 165 } 166 167 tParam := seln.Obj().Type().Underlying().(*types.Signature).Recv().Type() 168 return isPointer(tArg) && !isPointer(tParam) // implicit * 169 } 170 171 return seln.Indirect() 172 } 173 174 // effectiveReceiver returns the effective type of the method 175 // receiver after all implicit field selections (but not implicit * or 176 // & operations) have been applied. 177 // 178 // The boolean indicates whether any implicit field selection was indirect. 179 func effectiveReceiver(seln *types.Selection) (types.Type, bool) { 180 assert(seln.Kind() == types.MethodVal, "not MethodVal") 181 t := seln.Recv() 182 indices := seln.Index() 183 indirect := false 184 for _, index := range indices[:len(indices)-1] { 185 if isPointer(t) { 186 indirect = true 187 t = typeparams.MustDeref(t) 188 } 189 t = typeparams.CoreType(t).(*types.Struct).Field(index).Type() 190 } 191 return t, indirect 192 }