| // 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/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. |
| func trivialConversion(val types.Type, obj *types.Var) bool { |
| return types.Identical(types.Default(val), obj.Type()) |
| } |
| |
| 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 is a pointer type. |
| func isPointer(t types.Type) bool { return t != deref(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 tElem := deref(t); tElem != t { |
| indirect = true |
| t = tElem |
| } |
| t = typeparams.CoreType(t).(*types.Struct).Field(index).Type() |
| } |
| return t, indirect |
| } |