This fixes a bug in argument size calculation in the case where the function has no return values. Previously it was padding the argument struct to max alignment, but this only happens if there are return values following. Updates #191
174 lines
4.4 KiB
Go
174 lines
4.4 KiB
Go
package gotypes
|
|
|
|
import (
|
|
"go/token"
|
|
"go/types"
|
|
"strings"
|
|
"testing"
|
|
|
|
"golang.org/x/tools/go/packages"
|
|
)
|
|
|
|
func TestLookupSignature(t *testing.T) {
|
|
pkg := LoadPackageTypes(t, "math")
|
|
s, err := LookupSignature(pkg, "Frexp")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expect, err := ParseSignature("func(f float64) (frac float64, exp int)")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if s.String() != expect.String() {
|
|
t.Errorf("\n got: %s\nexpect: %s\n", s, expect)
|
|
}
|
|
}
|
|
|
|
func TestLookupSignatureErrors(t *testing.T) {
|
|
cases := []struct {
|
|
PackagePath string
|
|
FunctionName string
|
|
ExpectedError string
|
|
}{
|
|
{"runtime", "HmmIdk", "could not find function \"HmmIdk\""},
|
|
{"crypto", "Decrypter", "object \"Decrypter\" does not have signature type"},
|
|
{"encoding/base64", "StdEncoding", "object \"StdEncoding\" does not have signature type"},
|
|
}
|
|
for _, c := range cases {
|
|
pkg := LoadPackageTypes(t, c.PackagePath)
|
|
_, err := LookupSignature(pkg, c.FunctionName)
|
|
if err == nil {
|
|
t.Fatalf("expected error looking up '%s' in package '%s'", c.FunctionName, c.PackagePath)
|
|
}
|
|
if err.Error() != c.ExpectedError {
|
|
t.Fatalf("wrong error message\n got: %q\nexpect: %q", err.Error(), c.ExpectedError)
|
|
}
|
|
}
|
|
}
|
|
|
|
func LoadPackageTypes(t *testing.T, path string) *types.Package {
|
|
t.Helper()
|
|
cfg := &packages.Config{
|
|
Mode: packages.NeedTypes | packages.NeedDeps | packages.NeedImports,
|
|
}
|
|
pkgs, err := packages.Load(cfg, path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(pkgs) != 1 {
|
|
t.Fatal("expected to load exactly one package")
|
|
}
|
|
return pkgs[0].Types
|
|
}
|
|
|
|
func TestParseSignature(t *testing.T) {
|
|
cases := []struct {
|
|
Expr string
|
|
ExpectParams *types.Tuple
|
|
ExpectReturn *types.Tuple
|
|
}{
|
|
{
|
|
Expr: "func()",
|
|
},
|
|
{
|
|
Expr: "func(x, y uint64)",
|
|
ExpectParams: types.NewTuple(
|
|
types.NewParam(token.NoPos, nil, "x", types.Typ[types.Uint64]),
|
|
types.NewParam(token.NoPos, nil, "y", types.Typ[types.Uint64]),
|
|
),
|
|
},
|
|
{
|
|
Expr: "func(n int, s []string) byte",
|
|
ExpectParams: types.NewTuple(
|
|
types.NewParam(token.NoPos, nil, "n", types.Typ[types.Int]),
|
|
types.NewParam(token.NoPos, nil, "s", types.NewSlice(types.Typ[types.String])),
|
|
),
|
|
ExpectReturn: types.NewTuple(
|
|
types.NewParam(token.NoPos, nil, "", types.Typ[types.Byte]),
|
|
),
|
|
},
|
|
{
|
|
Expr: "func(x, y int) (x0, y0 int, s string)",
|
|
ExpectParams: types.NewTuple(
|
|
types.NewParam(token.NoPos, nil, "x", types.Typ[types.Int]),
|
|
types.NewParam(token.NoPos, nil, "y", types.Typ[types.Int]),
|
|
),
|
|
ExpectReturn: types.NewTuple(
|
|
types.NewParam(token.NoPos, nil, "x0", types.Typ[types.Int]),
|
|
types.NewParam(token.NoPos, nil, "y0", types.Typ[types.Int]),
|
|
types.NewParam(token.NoPos, nil, "s", types.Typ[types.String]),
|
|
),
|
|
},
|
|
}
|
|
for _, c := range cases {
|
|
s, err := ParseSignature(c.Expr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !TypesTuplesEqual(s.sig.Params(), c.ExpectParams) {
|
|
t.Errorf("parameter mismatch\ngot %#v\nexpect %#v\n", s.sig.Params(), c.ExpectParams)
|
|
}
|
|
if !TypesTuplesEqual(s.sig.Results(), c.ExpectReturn) {
|
|
t.Errorf("return value(s) mismatch\ngot %#v\nexpect %#v\n", s.sig.Results(), c.ExpectReturn)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseSignatureErrors(t *testing.T) {
|
|
cases := []struct {
|
|
Expr string
|
|
ErrorContains string
|
|
}{
|
|
{"idkjklol", "undeclared name"},
|
|
{"struct{}", "not a function signature"},
|
|
{"uint32(0xfeedbeef)", "should have nil value"},
|
|
}
|
|
for _, c := range cases {
|
|
s, err := ParseSignature(c.Expr)
|
|
if s != nil || err == nil || !strings.Contains(err.Error(), c.ErrorContains) {
|
|
t.Errorf("expect error from expression %s\ngot: %s\nexpect substring: %s\n", c.Expr, err, c.ErrorContains)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TypesTuplesEqual(a, b *types.Tuple) bool {
|
|
if a.Len() != b.Len() {
|
|
return false
|
|
}
|
|
n := a.Len()
|
|
for i := 0; i < n; i++ {
|
|
if !TypesVarsEqual(a.At(i), b.At(i)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func TypesVarsEqual(a, b *types.Var) bool {
|
|
return a.Name() == b.Name() && types.Identical(a.Type(), b.Type())
|
|
}
|
|
|
|
func TestSignatureSizes(t *testing.T) {
|
|
cases := []struct {
|
|
Expr string
|
|
ArgSize int
|
|
}{
|
|
{"func()", 0},
|
|
{"func(uint64) uint64", 16},
|
|
{"func([7]byte) byte", 9},
|
|
{"func(uint64, uint64) (uint64, uint64)", 32},
|
|
{"func(uint16)", 2},
|
|
}
|
|
for _, c := range cases {
|
|
s, err := ParseSignature(c.Expr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if s.Bytes() != c.ArgSize {
|
|
t.Errorf("%s: size %d expected %d", s, s.Bytes(), c.ArgSize)
|
|
}
|
|
}
|
|
}
|