зеркало из https://github.com/golang/tools.git
198 строки
5.6 KiB
Go
198 строки
5.6 KiB
Go
// 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
|
|
// 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 expression 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.
|
|
//
|
|
// Example: in func f() int32 { return 0 },
|
|
// the type recorded for 0 is int32, not untyped int;
|
|
// although it is Identical to the result var,
|
|
// the conversion is non-trivial.
|
|
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
|
|
}
|