Issue #77 pointed out that in the case of named array types, the gotypes Components type fails to recognize the underlying type as an array. In fact this problem was more widespread. This diff fixes the missing calls to Underlying() and adds some test cases. Fixes #77
229 lines
5.3 KiB
Go
229 lines
5.3 KiB
Go
package gotypes
|
|
|
|
import (
|
|
"go/token"
|
|
"go/types"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mmcloughlin/avo/reg"
|
|
|
|
"github.com/mmcloughlin/avo/operand"
|
|
)
|
|
|
|
func TestBasicKindsArePrimitive(t *testing.T) {
|
|
kinds := []types.BasicKind{
|
|
types.Bool,
|
|
types.Int,
|
|
types.Int8,
|
|
types.Int16,
|
|
types.Int32,
|
|
types.Int64,
|
|
types.Uint,
|
|
types.Uint8,
|
|
types.Uint16,
|
|
types.Uint32,
|
|
types.Uint64,
|
|
types.Uintptr,
|
|
types.Float32,
|
|
types.Float64,
|
|
}
|
|
for _, k := range kinds {
|
|
AssertPrimitive(t, types.Typ[k])
|
|
}
|
|
}
|
|
|
|
func TestPointersArePrimitive(t *testing.T) {
|
|
typ := types.NewPointer(types.Typ[types.Uint32])
|
|
AssertPrimitive(t, typ)
|
|
}
|
|
|
|
func AssertPrimitive(t *testing.T, typ types.Type) {
|
|
c := NewComponent(typ, operand.NewParamAddr("primitive", 0))
|
|
if _, err := c.Resolve(); err != nil {
|
|
t.Errorf("expected type %s to be primitive: got error '%s'", typ, err)
|
|
}
|
|
}
|
|
|
|
func TestComponentErrors(t *testing.T) {
|
|
comp := NewComponent(types.Typ[types.Uint32], operand.Mem{})
|
|
cases := []struct {
|
|
Component Component
|
|
ErrorSubstring string
|
|
}{
|
|
{comp.Base(), "only slices and strings"},
|
|
{comp.Len(), "only slices and strings"},
|
|
{comp.Cap(), "only slices have"},
|
|
{comp.Real(), "only complex"},
|
|
{comp.Imag(), "only complex"},
|
|
{comp.Index(42), "not array type"},
|
|
{comp.Field("a"), "not struct type"},
|
|
{comp.Dereference(reg.R12), "not pointer type"},
|
|
}
|
|
for _, c := range cases {
|
|
_, err := c.Component.Resolve()
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
if !strings.Contains(err.Error(), c.ErrorSubstring) {
|
|
t.Fatalf("error message %q; expected substring %q", err.Error(), c.ErrorSubstring)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestComponentErrorChaining(t *testing.T) {
|
|
// Build a component with an error.
|
|
comp := NewComponent(types.Typ[types.Uint32], operand.Mem{}).Index(3)
|
|
_, expect := comp.Resolve()
|
|
if expect == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
|
|
// Confirm that the error is preserved through chaining operations.
|
|
cases := []Component{
|
|
comp.Dereference(reg.R13),
|
|
comp.Base(),
|
|
comp.Len(),
|
|
comp.Cap(),
|
|
comp.Real(),
|
|
comp.Imag(),
|
|
comp.Index(42),
|
|
comp.Field("field"),
|
|
}
|
|
for _, c := range cases {
|
|
_, err := c.Resolve()
|
|
if err != expect {
|
|
t.Fatal("chaining should preserve error")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestComponentDeconstruction(t *testing.T) {
|
|
cases := []struct {
|
|
Name string
|
|
Type types.Type
|
|
Chain func(Component) Component
|
|
Param string
|
|
Offset int
|
|
}{
|
|
{
|
|
Name: "slice_base",
|
|
Type: types.NewSlice(types.Typ[types.Uint64]),
|
|
Chain: func(c Component) Component { return c.Base() },
|
|
Param: "base",
|
|
Offset: 0,
|
|
},
|
|
{
|
|
Name: "slice_len",
|
|
Type: types.NewSlice(types.Typ[types.Uint64]),
|
|
Chain: func(c Component) Component { return c.Len() },
|
|
Param: "len",
|
|
Offset: 8,
|
|
},
|
|
{
|
|
Name: "slice_cap",
|
|
Type: types.NewSlice(types.Typ[types.Uint64]),
|
|
Chain: func(c Component) Component { return c.Cap() },
|
|
Param: "cap",
|
|
Offset: 16,
|
|
},
|
|
{
|
|
Name: "string_base",
|
|
Type: types.Typ[types.String],
|
|
Chain: func(c Component) Component { return c.Base() },
|
|
Param: "base",
|
|
Offset: 0,
|
|
},
|
|
{
|
|
Name: "slice_len",
|
|
Type: types.Typ[types.String],
|
|
Chain: func(c Component) Component { return c.Len() },
|
|
Param: "len",
|
|
Offset: 8,
|
|
},
|
|
{
|
|
Name: "complex64_real",
|
|
Type: types.Typ[types.Complex64],
|
|
Chain: func(c Component) Component { return c.Real() },
|
|
Param: "real",
|
|
Offset: 0,
|
|
},
|
|
{
|
|
Name: "complex64_imag",
|
|
Type: types.Typ[types.Complex64],
|
|
Chain: func(c Component) Component { return c.Imag() },
|
|
Param: "imag",
|
|
Offset: 4,
|
|
},
|
|
{
|
|
Name: "complex128_real",
|
|
Type: types.Typ[types.Complex128],
|
|
Chain: func(c Component) Component { return c.Real() },
|
|
Param: "real",
|
|
Offset: 0,
|
|
},
|
|
{
|
|
Name: "complex128_imag",
|
|
Type: types.Typ[types.Complex128],
|
|
Chain: func(c Component) Component { return c.Imag() },
|
|
Param: "imag",
|
|
Offset: 8,
|
|
},
|
|
{
|
|
Name: "array",
|
|
Type: types.NewArray(types.Typ[types.Uint32], 7),
|
|
Chain: func(c Component) Component { return c.Index(3) },
|
|
Param: "3",
|
|
Offset: 12,
|
|
},
|
|
{
|
|
Name: "struct",
|
|
Type: types.NewStruct([]*types.Var{
|
|
types.NewField(token.NoPos, nil, "Byte", types.Typ[types.Byte], false),
|
|
types.NewField(token.NoPos, nil, "Uint64", types.Typ[types.Uint64], false),
|
|
}, nil),
|
|
Chain: func(c Component) Component { return c.Field("Uint64") },
|
|
Param: "Uint64",
|
|
Offset: 8,
|
|
},
|
|
}
|
|
|
|
// For every test case, generate the same case but when the type is wrapped in
|
|
// a named type.
|
|
n := len(cases)
|
|
for i := 0; i < n; i++ {
|
|
wrapped := cases[i]
|
|
wrapped.Name += "_wrapped"
|
|
wrapped.Type = types.NewNamed(
|
|
types.NewTypeName(token.NoPos, nil, "wrapped", nil),
|
|
wrapped.Type,
|
|
nil,
|
|
)
|
|
cases = append(cases, wrapped)
|
|
}
|
|
|
|
for _, c := range cases {
|
|
c := c // avoid scopelint error
|
|
t.Run(c.Name, func(t *testing.T) {
|
|
t.Log(c.Type)
|
|
base := operand.NewParamAddr("test", 0)
|
|
comp := NewComponent(c.Type, base)
|
|
comp = c.Chain(comp)
|
|
|
|
b, err := comp.Resolve()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectname := "test_" + c.Param
|
|
if b.Addr.Symbol.Name != expectname {
|
|
t.Errorf("parameter name %q; expected %q", b.Addr.Symbol.Name, expectname)
|
|
}
|
|
|
|
if b.Addr.Disp != c.Offset {
|
|
t.Errorf("offset %d; expected %d", b.Addr.Disp, c.Offset)
|
|
}
|
|
})
|
|
}
|
|
}
|