From a024924d60a2cef7574043441d7e030065a71d28 Mon Sep 17 00:00:00 2001 From: Reilly Watson Date: Fri, 23 Aug 2024 15:59:35 -0400 Subject: [PATCH 1/3] support imported type aliases Go 1.23 changed how go/types handles type aliases, which breaks code generation if you use a package that has an alias type (example: https://pkg.go.dev/github.com/golang/protobuf@v1.5.3/ptypes/timestamp#Timestamp). These were getting generated as just the unqualified name, without the package. If you used a previous version of Go, it meant that if that aliased type was in an internal package we'd try to import the internal package instead, resulting in a compilation error. Add a switch to handle that case, and bump the go.mod version to support using types.Alias. --- go.mod | 2 +- internal/registry/method_scope.go | 12 ++++++++++++ pkg/moq/moq_test.go | 16 ++++++++++++++++ pkg/moq/testpackages/typealias/typealias.go | 9 +++++++++ .../typealiasinternal/typealiasinternal.go | 6 ++++++ .../testpackages/typealiastwo/typealiastwo.go | 5 +++++ 6 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 pkg/moq/testpackages/typealias/typealias.go create mode 100644 pkg/moq/testpackages/typealiastwo/internal/typealiasinternal/typealiasinternal.go create mode 100644 pkg/moq/testpackages/typealiastwo/typealiastwo.go diff --git a/go.mod b/go.mod index e04213b..79d0b70 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/matryer/moq -go 1.22.5 +go 1.23 require ( github.com/pmezard/go-difflib v1.0.0 diff --git a/internal/registry/method_scope.go b/internal/registry/method_scope.go index 65b5616..8cfd095 100644 --- a/internal/registry/method_scope.go +++ b/internal/registry/method_scope.go @@ -90,6 +90,18 @@ func (m MethodScope) populateImports(t types.Type, imports map[string]*Package) } } + case *types.Alias: + if pkg := t.Obj().Pkg(); pkg != nil { + imports[stripVendorPath(pkg.Path())] = m.registry.AddImport(pkg) + } + // The imports of a Type with a TypeList must be added to the imports list + // For example: Foo[otherpackage.Bar] , must have otherpackage imported + if targs := t.TypeArgs(); targs != nil { + for i := 0; i < targs.Len(); i++ { + m.populateImports(targs.At(i), imports) + } + } + case *types.Array: m.populateImports(t.Elem(), imports) diff --git a/pkg/moq/moq_test.go b/pkg/moq/moq_test.go index 5d6230d..4721173 100644 --- a/pkg/moq/moq_test.go +++ b/pkg/moq/moq_test.go @@ -664,6 +664,22 @@ func TestImportedPackageWithSameName(t *testing.T) { } } +func TestImportedPackageWithTypeAlias(t *testing.T) { + m, err := New(Config{SrcDir: "testpackages/typealias"}) + if err != nil { + t.Fatalf("moq.New: %s", err) + } + var buf bytes.Buffer + err = m.Mock(&buf, "Example") + if err != nil { + t.Errorf("mock error: %s", err) + } + s := buf.String() + if !strings.Contains(s, `a typealiastwo.AliasType`) { + t.Error("missing typealiastwo.AliasType") + } +} + func TestParseError(t *testing.T) { _, err := New(Config{SrcDir: "testpackages/_parseerror/service"}) if err == nil { diff --git a/pkg/moq/testpackages/typealias/typealias.go b/pkg/moq/testpackages/typealias/typealias.go new file mode 100644 index 0000000..5065678 --- /dev/null +++ b/pkg/moq/testpackages/typealias/typealias.go @@ -0,0 +1,9 @@ +package typealias + +import ( + "github.com/matryer/moq/pkg/moq/testpackages/typealiastwo" +) + +type Example interface { + Do(a typealiastwo.AliasType) error +} diff --git a/pkg/moq/testpackages/typealiastwo/internal/typealiasinternal/typealiasinternal.go b/pkg/moq/testpackages/typealiastwo/internal/typealiasinternal/typealiasinternal.go new file mode 100644 index 0000000..6b87f80 --- /dev/null +++ b/pkg/moq/testpackages/typealiastwo/internal/typealiasinternal/typealiasinternal.go @@ -0,0 +1,6 @@ +package typealiasinternal + +// we shouldn't be able to import this type directly, you need to use the alias in the parent package +type MyInternalType struct { + Foo int +} diff --git a/pkg/moq/testpackages/typealiastwo/typealiastwo.go b/pkg/moq/testpackages/typealiastwo/typealiastwo.go new file mode 100644 index 0000000..53a47c1 --- /dev/null +++ b/pkg/moq/testpackages/typealiastwo/typealiastwo.go @@ -0,0 +1,5 @@ +package typealiastwo + +import "github.com/matryer/moq/pkg/moq/testpackages/typealiastwo/internal/typealiasinternal" + +type AliasType = typealiasinternal.MyInternalType From c7f4a318114578f4a312b8a653a6ce47e62533d1 Mon Sep 17 00:00:00 2001 From: Reilly Watson Date: Fri, 23 Aug 2024 23:18:16 -0400 Subject: [PATCH 2/3] Update to use a golden file test, instead of just comparing a string --- pkg/moq/moq_test.go | 22 ++---- .../typealias/typealias_moq.golden.go | 75 +++++++++++++++++++ 2 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 pkg/moq/testpackages/typealias/typealias_moq.golden.go diff --git a/pkg/moq/moq_test.go b/pkg/moq/moq_test.go index 4721173..1e31cfb 100644 --- a/pkg/moq/moq_test.go +++ b/pkg/moq/moq_test.go @@ -412,6 +412,12 @@ func TestMockGolden(t *testing.T) { interfaces: []string{"Magician"}, goldenFile: filepath.Join("testpackages/rangenum", "rangenum_moq.golden.go"), }, + { + name: "TypeAlias", + cfg: Config{SrcDir: "testpackages/typealias"}, + interfaces: []string{"Example"}, + goldenFile: filepath.Join("testpackages/typealias", "typealias_moq.golden.go"), + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { @@ -664,22 +670,6 @@ func TestImportedPackageWithSameName(t *testing.T) { } } -func TestImportedPackageWithTypeAlias(t *testing.T) { - m, err := New(Config{SrcDir: "testpackages/typealias"}) - if err != nil { - t.Fatalf("moq.New: %s", err) - } - var buf bytes.Buffer - err = m.Mock(&buf, "Example") - if err != nil { - t.Errorf("mock error: %s", err) - } - s := buf.String() - if !strings.Contains(s, `a typealiastwo.AliasType`) { - t.Error("missing typealiastwo.AliasType") - } -} - func TestParseError(t *testing.T) { _, err := New(Config{SrcDir: "testpackages/_parseerror/service"}) if err == nil { diff --git a/pkg/moq/testpackages/typealias/typealias_moq.golden.go b/pkg/moq/testpackages/typealias/typealias_moq.golden.go new file mode 100644 index 0000000..f9c0fbe --- /dev/null +++ b/pkg/moq/testpackages/typealias/typealias_moq.golden.go @@ -0,0 +1,75 @@ +// Code generated by moq; DO NOT EDIT. +// github.com/matryer/moq + +package typealias + +import ( + "github.com/matryer/moq/pkg/moq/testpackages/typealiastwo" + "sync" +) + +// Ensure, that ExampleMock does implement Example. +// If this is not the case, regenerate this file with moq. +var _ Example = &ExampleMock{} + +// ExampleMock is a mock implementation of Example. +// +// func TestSomethingThatUsesExample(t *testing.T) { +// +// // make and configure a mocked Example +// mockedExample := &ExampleMock{ +// DoFunc: func(a typealiastwo.AliasType) error { +// panic("mock out the Do method") +// }, +// } +// +// // use mockedExample in code that requires Example +// // and then make assertions. +// +// } +type ExampleMock struct { + // DoFunc mocks the Do method. + DoFunc func(a typealiastwo.AliasType) error + + // calls tracks calls to the methods. + calls struct { + // Do holds details about calls to the Do method. + Do []struct { + // A is the a argument value. + A typealiastwo.AliasType + } + } + lockDo sync.RWMutex +} + +// Do calls DoFunc. +func (mock *ExampleMock) Do(a typealiastwo.AliasType) error { + if mock.DoFunc == nil { + panic("ExampleMock.DoFunc: method is nil but Example.Do was just called") + } + callInfo := struct { + A typealiastwo.AliasType + }{ + A: a, + } + mock.lockDo.Lock() + mock.calls.Do = append(mock.calls.Do, callInfo) + mock.lockDo.Unlock() + return mock.DoFunc(a) +} + +// DoCalls gets all the calls that were made to Do. +// Check the length with: +// +// len(mockedExample.DoCalls()) +func (mock *ExampleMock) DoCalls() []struct { + A typealiastwo.AliasType +} { + var calls []struct { + A typealiastwo.AliasType + } + mock.lockDo.RLock() + calls = mock.calls.Do + mock.lockDo.RUnlock() + return calls +} From c110da7c0c342d1ea996a304d025ab6ee432d5cd Mon Sep 17 00:00:00 2001 From: Reilly Watson Date: Fri, 23 Aug 2024 23:29:25 -0400 Subject: [PATCH 3/3] validate we can use aliases with type arguments --- pkg/moq/testpackages/typealias/typealias.go | 2 +- .../testpackages/typealias/typealias_moq.golden.go | 14 ++++++++++---- .../typealiasinternal/typealiasinternal.go | 6 +++++- pkg/moq/testpackages/typealiastwo/typealiastwo.go | 2 ++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/pkg/moq/testpackages/typealias/typealias.go b/pkg/moq/testpackages/typealias/typealias.go index 5065678..619fc99 100644 --- a/pkg/moq/testpackages/typealias/typealias.go +++ b/pkg/moq/testpackages/typealias/typealias.go @@ -5,5 +5,5 @@ import ( ) type Example interface { - Do(a typealiastwo.AliasType) error + Do(a typealiastwo.AliasType, b typealiastwo.GenericAliasType) error } diff --git a/pkg/moq/testpackages/typealias/typealias_moq.golden.go b/pkg/moq/testpackages/typealias/typealias_moq.golden.go index f9c0fbe..225a35c 100644 --- a/pkg/moq/testpackages/typealias/typealias_moq.golden.go +++ b/pkg/moq/testpackages/typealias/typealias_moq.golden.go @@ -18,7 +18,7 @@ var _ Example = &ExampleMock{} // // // make and configure a mocked Example // mockedExample := &ExampleMock{ -// DoFunc: func(a typealiastwo.AliasType) error { +// DoFunc: func(a typealiastwo.AliasType, b typealiastwo.GenericAliasType) error { // panic("mock out the Do method") // }, // } @@ -29,7 +29,7 @@ var _ Example = &ExampleMock{} // } type ExampleMock struct { // DoFunc mocks the Do method. - DoFunc func(a typealiastwo.AliasType) error + DoFunc func(a typealiastwo.AliasType, b typealiastwo.GenericAliasType) error // calls tracks calls to the methods. calls struct { @@ -37,25 +37,29 @@ type ExampleMock struct { Do []struct { // A is the a argument value. A typealiastwo.AliasType + // B is the b argument value. + B typealiastwo.GenericAliasType } } lockDo sync.RWMutex } // Do calls DoFunc. -func (mock *ExampleMock) Do(a typealiastwo.AliasType) error { +func (mock *ExampleMock) Do(a typealiastwo.AliasType, b typealiastwo.GenericAliasType) error { if mock.DoFunc == nil { panic("ExampleMock.DoFunc: method is nil but Example.Do was just called") } callInfo := struct { A typealiastwo.AliasType + B typealiastwo.GenericAliasType }{ A: a, + B: b, } mock.lockDo.Lock() mock.calls.Do = append(mock.calls.Do, callInfo) mock.lockDo.Unlock() - return mock.DoFunc(a) + return mock.DoFunc(a, b) } // DoCalls gets all the calls that were made to Do. @@ -64,9 +68,11 @@ func (mock *ExampleMock) Do(a typealiastwo.AliasType) error { // len(mockedExample.DoCalls()) func (mock *ExampleMock) DoCalls() []struct { A typealiastwo.AliasType + B typealiastwo.GenericAliasType } { var calls []struct { A typealiastwo.AliasType + B typealiastwo.GenericAliasType } mock.lockDo.RLock() calls = mock.calls.Do diff --git a/pkg/moq/testpackages/typealiastwo/internal/typealiasinternal/typealiasinternal.go b/pkg/moq/testpackages/typealiastwo/internal/typealiasinternal/typealiasinternal.go index 6b87f80..c0b80de 100644 --- a/pkg/moq/testpackages/typealiastwo/internal/typealiasinternal/typealiasinternal.go +++ b/pkg/moq/testpackages/typealiastwo/internal/typealiasinternal/typealiasinternal.go @@ -1,6 +1,10 @@ package typealiasinternal -// we shouldn't be able to import this type directly, you need to use the alias in the parent package +// we shouldn't be able to import these types directly, you need to use the alias in the parent package type MyInternalType struct { Foo int } + +type MyGenericType[T any] struct { + A T +} diff --git a/pkg/moq/testpackages/typealiastwo/typealiastwo.go b/pkg/moq/testpackages/typealiastwo/typealiastwo.go index 53a47c1..de24951 100644 --- a/pkg/moq/testpackages/typealiastwo/typealiastwo.go +++ b/pkg/moq/testpackages/typealiastwo/typealiastwo.go @@ -3,3 +3,5 @@ package typealiastwo import "github.com/matryer/moq/pkg/moq/testpackages/typealiastwo/internal/typealiasinternal" type AliasType = typealiasinternal.MyInternalType + +type GenericAliasType = typealiasinternal.MyGenericType[int]