// 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 }