Repository: mmcloughlin/avo
Pull request #78 (2019-04-13 22:04:31)

gotypes: add missing Underlying() calls
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.

codecov-io: # [Codecov](https://codecov.io/gh/mmcloughlin/avo/pull/78?src=pr&el=h1) Report
> Merging [#78](https://codecov.io/gh/mmcloughlin/avo/pull/78?src=pr&el=desc) into [master](https://codecov.io/gh/mmcloughlin/avo/commit/138eaf8dc34d1c265aab931e3541149d667426ca?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/mmcloughlin/avo/pull/78/graphs/tree.svg?width=650&token=DaOLKxzdQS&height=150&src=pr)](https://codecov.io/gh/mmcloughlin/avo/pull/78?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master      #78   +/-   ##
=======================================
  Coverage   76.01%   76.01%           
=======================================
  Files          51       51           
  Lines       31136    31136           
=======================================
  Hits        23669    23669           
  Misses       7367     7367           
  Partials      100      100
```

| Flag | Coverage Δ | |
|---|---|---|
| #integration | `6.08% <100%> (ø)` | :arrow_up: |
| #unittests | `74.24% <100%> (+0.09%)` | :arrow_up: |

| [Impacted Files](https://codecov.io/gh/mmcloughlin/avo/pull/78?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [gotypes/components.go](https://codecov.io/gh/mmcloughlin/avo/pull/78/diff?src=pr&el=tree#diff-Z290eXBlcy9jb21wb25lbnRzLmdv) | `93.2% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mmcloughlin/avo/pull/78?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mmcloughlin/avo/pull/78?src=pr&el=footer). Last update [138eaf8...9b7e9e4](https://codecov.io/gh/mmcloughlin/avo/pull/78?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/gotypes/components.go b/gotypes/components.go
index dd224b3..2206afa 100644
--- a/gotypes/components.go
+++ b/gotypes/components.go
@@ -143,7 +143,7 @@ func (c *component) Imag() Component {
 }
 
 func (c *component) Index(i int) Component {
-	a, ok := c.typ.(*types.Array)
+	a, ok := c.typ.Underlying().(*types.Array)
 	if !ok {
 		return errorf("not array type")
 	}
@@ -213,17 +213,17 @@ func (c *component) sub(suffix string, offset int, t types.Type) *component {
 }
 
 func isslice(t types.Type) bool {
-	_, ok := t.(*types.Slice)
+	_, ok := t.Underlying().(*types.Slice)
 	return ok
 }
 
 func isstring(t types.Type) bool {
-	b, ok := t.(*types.Basic)
+	b, ok := t.Underlying().(*types.Basic)
 	return ok && b.Kind() == types.String
 }
 
 func iscomplex(t types.Type) bool {
-	b, ok := t.(*types.Basic)
+	b, ok := t.Underlying().(*types.Basic)
 	return ok && (b.Info()&types.IsComplex) != 0
 }
 


Test patch:
diff --git a/gotypes/components_test.go b/gotypes/components_test.go
index b0b2366..2a045e0 100644
--- a/gotypes/components_test.go
+++ b/gotypes/components_test.go
@@ -1,6 +1,7 @@
 package gotypes
 
 import (
+	"go/token"
 	"go/types"
 	"strings"
 	"testing"
@@ -96,3 +97,132 @@ func TestComponentErrorChaining(t *testing.T) {
 		}
 	}
 }
+
+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)
+			}
+		})
+	}
+}