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  }