| // Copyright 2023 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 inline | |
| // This file defines various common helpers. | |
| import ( | |
| "go/ast" | |
| "go/constant" | |
| "go/token" | |
| "go/types" | |
| "reflect" | |
| "strings" | |
| "golang.org/x/tools/internal/typeparams" | |
| ) | |
| func is[T any](x any) bool { | |
| _, ok := x.(T) | |
| return ok | |
| } | |
| // TODO(adonovan): use go1.21's slices.Clone. | |
| func clone[T any](slice []T) []T { return append([]T{}, slice...) } | |
| // TODO(adonovan): use go1.21's slices.Index. | |
| func index[T comparable](slice []T, x T) int { | |
| for i, elem := range slice { | |
| if elem == x { | |
| return i | |
| } | |
| } | |
| return -1 | |
| } | |
| func btoi(b bool) int { | |
| if b { | |
| return 1 | |
| } else { | |
| return 0 | |
| } | |
| } | |
| func offsetOf(fset *token.FileSet, pos token.Pos) int { | |
| return fset.PositionFor(pos, false).Offset | |
| } | |
| // objectKind returns an object's kind (e.g. var, func, const, typename). | |
| func objectKind(obj types.Object) string { | |
| return strings.TrimPrefix(strings.ToLower(reflect.TypeOf(obj).String()), "*types.") | |
| } | |
| // within reports whether pos is within the half-open interval [n.Pos, n.End). | |
| func within(pos token.Pos, n ast.Node) bool { | |
| return n.Pos() <= pos && pos < n.End() | |
| } | |
| // trivialConversion reports whether it is safe to omit the implicit | |
| // value-to-variable conversion that occurs in argument passing or | |
| // result return. The only case currently allowed is converting from | |
| // untyped constant to its default type (e.g. 0 to int). | |
| // | |
| // The reason for this check is that converting from A to B to C may | |
| // yield a different result than converting A directly to C: consider | |
| // 0 to int32 to any. | |
| // | |
| // trivialConversion under-approximates trivial conversions, as unfortunately | |
| // go/types does not record the type of an expression *before* it is implicitly | |
| // converted, and therefore it cannot distinguish typed constant constant | |
| // expressions from untyped constant expressions. For example, in the | |
| // expression `c + 2`, where c is a uint32 constant, trivialConversion does not | |
| // detect that the default type of this express is actually uint32, not untyped | |
| // int. | |
| // | |
| // We could, of course, do better here by reverse engineering some of go/types' | |
| // constant handling. That may or may not be worthwhile.. | |
| func trivialConversion(fromValue constant.Value, from, to types.Type) bool { | |
| if fromValue != nil { | |
| var defaultType types.Type | |
| switch fromValue.Kind() { | |
| case constant.Bool: | |
| defaultType = types.Typ[types.Bool] | |
| case constant.String: | |
| defaultType = types.Typ[types.String] | |
| case constant.Int: | |
| defaultType = types.Typ[types.Int] | |
| case constant.Float: | |
| defaultType = types.Typ[types.Float64] | |
| case constant.Complex: | |
| defaultType = types.Typ[types.Complex128] | |
| default: | |
| return false | |
| } | |
| return types.Identical(defaultType, to) | |
| } | |
| return types.Identical(from, to) | |
| } | |
| func checkInfoFields(info *types.Info) { | |
| assert(info.Defs != nil, "types.Info.Defs is nil") | |
| assert(info.Implicits != nil, "types.Info.Implicits is nil") | |
| assert(info.Scopes != nil, "types.Info.Scopes is nil") | |
| assert(info.Selections != nil, "types.Info.Selections is nil") | |
| assert(info.Types != nil, "types.Info.Types is nil") | |
| assert(info.Uses != nil, "types.Info.Uses is nil") | |
| } | |
| func funcHasTypeParams(decl *ast.FuncDecl) bool { | |
| // generic function? | |
| if decl.Type.TypeParams != nil { | |
| return true | |
| } | |
| // method on generic type? | |
| if decl.Recv != nil { | |
| t := decl.Recv.List[0].Type | |
| if u, ok := t.(*ast.StarExpr); ok { | |
| t = u.X | |
| } | |
| return is[*ast.IndexExpr](t) || is[*ast.IndexListExpr](t) | |
| } | |
| return false | |
| } | |
| // intersects reports whether the maps' key sets intersect. | |
| func intersects[K comparable, T1, T2 any](x map[K]T1, y map[K]T2) bool { | |
| if len(x) > len(y) { | |
| return intersects(y, x) | |
| } | |
| for k := range x { | |
| if _, ok := y[k]; ok { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| // convert returns syntax for the conversion T(x). | |
| func convert(T, x ast.Expr) *ast.CallExpr { | |
| // The formatter generally adds parens as needed, | |
| // but before go1.22 it had a bug (#63362) for | |
| // channel types that requires this workaround. | |
| if ch, ok := T.(*ast.ChanType); ok && ch.Dir == ast.RECV { | |
| T = &ast.ParenExpr{X: T} | |
| } | |
| return &ast.CallExpr{ | |
| Fun: T, | |
| Args: []ast.Expr{x}, | |
| } | |
| } | |
| // isPointer reports whether t's core type is a pointer. | |
| func isPointer(t types.Type) bool { | |
| return is[*types.Pointer](typeparams.CoreType(t)) | |
| } | |
| // indirectSelection is like seln.Indirect() without bug #8353. | |
| func indirectSelection(seln *types.Selection) bool { | |
| // Work around bug #8353 in Selection.Indirect when Kind=MethodVal. | |
| if seln.Kind() == types.MethodVal { | |
| tArg, indirect := effectiveReceiver(seln) | |
| if indirect { | |
| return true | |
| } | |
| tParam := seln.Obj().Type().Underlying().(*types.Signature).Recv().Type() | |
| return isPointer(tArg) && !isPointer(tParam) // implicit * | |
| } | |
| return seln.Indirect() | |
| } | |
| // effectiveReceiver returns the effective type of the method | |
| // receiver after all implicit field selections (but not implicit * or | |
| // & operations) have been applied. | |
| // | |
| // The boolean indicates whether any implicit field selection was indirect. | |
| func effectiveReceiver(seln *types.Selection) (types.Type, bool) { | |
| assert(seln.Kind() == types.MethodVal, "not MethodVal") | |
| t := seln.Recv() | |
| indices := seln.Index() | |
| indirect := false | |
| for _, index := range indices[:len(indices)-1] { | |
| if isPointer(t) { | |
| indirect = true | |
| t = typeparams.MustDeref(t) | |
| } | |
| t = typeparams.CoreType(t).(*types.Struct).Field(index).Type() | |
| } | |
| return t, indirect | |
| } |