From de46f6d2325df19f931e5b6086d29a1d2a187612 Mon Sep 17 00:00:00 2001 From: Tulskiy Aleksandr Date: Mon, 22 Sep 2025 20:31:21 +0300 Subject: [PATCH 1/2] Added support for mock generation for function signatures --- README.md | 41 +- .../internal/tests/signature/checks/checks.go | 57 +++ .../signature/package_mode/external_mock.go | 136 ++++++ .../tests/signature/package_mode/mock.go | 448 ++++++++++++++++++ .../internal/tests/signature/signatures.go | 25 + .../tests/signature/source_mode/mock.go | 202 ++++++++ mockgen/package_mode.go | 59 ++- mockgen/parse.go | 100 ++++ 8 files changed, 1044 insertions(+), 24 deletions(-) create mode 100644 mockgen/internal/tests/signature/checks/checks.go create mode 100644 mockgen/internal/tests/signature/package_mode/external_mock.go create mode 100644 mockgen/internal/tests/signature/package_mode/mock.go create mode 100644 mockgen/internal/tests/signature/signatures.go create mode 100644 mockgen/internal/tests/signature/source_mode/mock.go diff --git a/README.md b/README.md index 4ab94d8..8ebc055 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ mockgen -archive=pkg.a database/sql/driver Conn,Driver ### Source mode -Source mode generates mock interfaces from a source file. +Source mode generates mock interfaces and signatures from a source file. It is enabled by using the -source flag. Other flags that may be useful in this mode are -imports and -aux_files. @@ -72,7 +72,7 @@ mockgen -source=foo.go [other options] ### Package mode -Package mode works by specifying the package and interface names. +Package mode works by specifying the package and interface or signature names. It is enabled by passing two non-flag arguments: an import path, and a comma-separated list of symbols. @@ -85,6 +85,9 @@ mockgen database/sql/driver Conn,Driver # Convenient for `go:generate`. mockgen . Conn,Driver + +# Function signatures can also be mocked +mockgen iter Seq,Seq2 ``` ### Flags @@ -151,6 +154,8 @@ cases, you will need only the `-source` flag. ## Building Mocks +## Interfaces + ```go type Foo interface { Bar(x int) int @@ -179,6 +184,38 @@ func TestFoo(t *testing.T) { } ``` +### Signatures + +```go +import "iter" + +type ToString[T any] func(T) string + +func ToStringArray[T any](array []T, toString ToString[T]) []string { + // ... +} + +``` + +```go +func TestToStringArray(t *testing.T) { + ctrl := gomock.NewController(t) + + m := NewMockToString[int](ctrl) + + // The Execute method is used as an implementation of the ToString signature. + m. + EXPECT(). + Execute(gomock.AnyOf(1, 2)). + DoAndReturn(strconv.Itoa). + Times(2) + + strArray := ToStringArray([]int{1, 2}, m.Execute) + + fmt.Println(strArray) // ["1", "2"] +} +``` + ## Building Stubs ```go diff --git a/mockgen/internal/tests/signature/checks/checks.go b/mockgen/internal/tests/signature/checks/checks.go new file mode 100644 index 0000000..99ae039 --- /dev/null +++ b/mockgen/internal/tests/signature/checks/checks.go @@ -0,0 +1,57 @@ +package main + +import ( + "iter" + + "go.uber.org/mock/mockgen/internal/tests/signature" + "go.uber.org/mock/mockgen/internal/tests/signature/package_mode" + "go.uber.org/mock/mockgen/internal/tests/signature/source_mode" +) + +func UseSome(fnc signature.Some) {} + +func UseSome2(fnc signature.Some2) {} + +func UseGeneric[T any](fnc signature.Generic[T]) {} + +func UseWithMethod(fnc signature.WithMethod) {} + +func UseSeq[T any](fnc iter.Seq[T]) {} + +func packageModeChecks() { + UseSome((&package_mode.MockSome{}).Execute) + UseSome((&package_mode.MockSome2{}).Execute) + UseSome((&package_mode.MockAlias{}).Execute) + UseSome2((&package_mode.MockSome{}).Execute) + UseSome2((&package_mode.MockSome2{}).Execute) + UseSome2((&package_mode.MockAlias{}).Execute) + + UseGeneric((&package_mode.MockGeneric[int]{}).Execute) + UseGeneric[int]((&package_mode.MockIntGeneric{}).Execute) + + UseWithMethod((&package_mode.MockWithMethod{}).Execute) + + UseSeq((&package_mode.MockIntIter{}).Execute) + UseSeq((&package_mode.MockSeq[bool]{}).Execute) +} + +func sourceModeChecks() { + // Source Mode currently does not support aliases and derived types. + // Please uncomment when the opportunity arises. + //UseSome((&source_mode.MockSome2{}).Execute) + //UseSome((&source_mode.MockAlias{}).Execute) + //UseSome2((&source_mode.MockSome2{}).Execute) + //UseSome2((&source_mode.MockAlias{}).Execute) + //UseGeneric[int]((&source_mode.MockIntGeneric{}).Execute) + + UseSome((&source_mode.MockSome{}).Execute) + UseSome2((&source_mode.MockSome{}).Execute) + UseGeneric((&source_mode.MockGeneric[int]{}).Execute) + UseWithMethod((&source_mode.MockWithMethod{}).Execute) +} + +// We check in compile-time that mocks are generated correctly +func main() { + packageModeChecks() + sourceModeChecks() +} diff --git a/mockgen/internal/tests/signature/package_mode/external_mock.go b/mockgen/internal/tests/signature/package_mode/external_mock.go new file mode 100644 index 0000000..7a1bc7f --- /dev/null +++ b/mockgen/internal/tests/signature/package_mode/external_mock.go @@ -0,0 +1,136 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: iter (interfaces: Seq,Seq2) +// +// Generated by this command: +// +// ___funcitons_package_mode_external -typed -destination=package_mode/external_mock.go -package=package_mode iter Seq,Seq2 +// + +// Package package_mode is a generated GoMock package. +package package_mode + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockSeq is a mock of Seq interface. +type MockSeq[V any] struct { + ctrl *gomock.Controller + recorder *MockSeqMockRecorder[V] + isgomock struct{} +} + +// MockSeqMockRecorder is the mock recorder for MockSeq. +type MockSeqMockRecorder[V any] struct { + mock *MockSeq[V] +} + +// NewMockSeq creates a new mock instance. +func NewMockSeq[V any](ctrl *gomock.Controller) *MockSeq[V] { + mock := &MockSeq[V]{ctrl: ctrl} + mock.recorder = &MockSeqMockRecorder[V]{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSeq[V]) EXPECT() *MockSeqMockRecorder[V] { + return m.recorder +} + +// Execute mocks base method. +func (m *MockSeq[V]) Execute(yield func(V) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Execute", yield) +} + +// Execute indicates an expected call of Execute. +func (mr *MockSeqMockRecorder[V]) Execute(yield any) *MockSeqExecuteCall[V] { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockSeq[V])(nil).Execute), yield) + return &MockSeqExecuteCall[V]{Call: call} +} + +// MockSeqExecuteCall wrap *gomock.Call +type MockSeqExecuteCall[V any] struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockSeqExecuteCall[V]) Return() *MockSeqExecuteCall[V] { + c.Call = c.Call.Return() + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockSeqExecuteCall[V]) Do(f func(func(V) bool)) *MockSeqExecuteCall[V] { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockSeqExecuteCall[V]) DoAndReturn(f func(func(V) bool)) *MockSeqExecuteCall[V] { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// MockSeq2 is a mock of Seq2 interface. +type MockSeq2[K any, V any] struct { + ctrl *gomock.Controller + recorder *MockSeq2MockRecorder[K, V] + isgomock struct{} +} + +// MockSeq2MockRecorder is the mock recorder for MockSeq2. +type MockSeq2MockRecorder[K any, V any] struct { + mock *MockSeq2[K, V] +} + +// NewMockSeq2 creates a new mock instance. +func NewMockSeq2[K any, V any](ctrl *gomock.Controller) *MockSeq2[K, V] { + mock := &MockSeq2[K, V]{ctrl: ctrl} + mock.recorder = &MockSeq2MockRecorder[K, V]{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSeq2[K, V]) EXPECT() *MockSeq2MockRecorder[K, V] { + return m.recorder +} + +// Execute mocks base method. +func (m *MockSeq2[K, V]) Execute(yield func(K, V) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Execute", yield) +} + +// Execute indicates an expected call of Execute. +func (mr *MockSeq2MockRecorder[K, V]) Execute(yield any) *MockSeq2ExecuteCall[K, V] { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockSeq2[K, V])(nil).Execute), yield) + return &MockSeq2ExecuteCall[K, V]{Call: call} +} + +// MockSeq2ExecuteCall wrap *gomock.Call +type MockSeq2ExecuteCall[K any, V any] struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockSeq2ExecuteCall[K, V]) Return() *MockSeq2ExecuteCall[K, V] { + c.Call = c.Call.Return() + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockSeq2ExecuteCall[K, V]) Do(f func(func(K, V) bool)) *MockSeq2ExecuteCall[K, V] { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockSeq2ExecuteCall[K, V]) DoAndReturn(f func(func(K, V) bool)) *MockSeq2ExecuteCall[K, V] { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/mockgen/internal/tests/signature/package_mode/mock.go b/mockgen/internal/tests/signature/package_mode/mock.go new file mode 100644 index 0000000..225f718 --- /dev/null +++ b/mockgen/internal/tests/signature/package_mode/mock.go @@ -0,0 +1,448 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: go.uber.org/mock/mockgen/internal/tests/signature (interfaces: Some,Some2,Alias,Generic,IntGeneric,WithMethod,IntIter) +// +// Generated by this command: +// +// ___funtions_package_mode -typed -destination=package_mode/mock.go -package=package_mode . Some,Some2,Alias,Generic,IntGeneric,WithMethod,IntIter +// + +// Package package_mode is a generated GoMock package. +package package_mode + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockSome is a mock of Some interface. +type MockSome struct { + ctrl *gomock.Controller + recorder *MockSomeMockRecorder + isgomock struct{} +} + +// MockSomeMockRecorder is the mock recorder for MockSome. +type MockSomeMockRecorder struct { + mock *MockSome +} + +// NewMockSome creates a new mock instance. +func NewMockSome(ctrl *gomock.Controller) *MockSome { + mock := &MockSome{ctrl: ctrl} + mock.recorder = &MockSomeMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSome) EXPECT() *MockSomeMockRecorder { + return m.recorder +} + +// Execute mocks base method. +func (m *MockSome) Execute() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute") + ret0, _ := ret[0].(string) + return ret0 +} + +// Execute indicates an expected call of Execute. +func (mr *MockSomeMockRecorder) Execute() *MockSomeExecuteCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockSome)(nil).Execute)) + return &MockSomeExecuteCall{Call: call} +} + +// MockSomeExecuteCall wrap *gomock.Call +type MockSomeExecuteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockSomeExecuteCall) Return(arg0 string) *MockSomeExecuteCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockSomeExecuteCall) Do(f func() string) *MockSomeExecuteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockSomeExecuteCall) DoAndReturn(f func() string) *MockSomeExecuteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// MockSome2 is a mock of Some2 interface. +type MockSome2 struct { + ctrl *gomock.Controller + recorder *MockSome2MockRecorder + isgomock struct{} +} + +// MockSome2MockRecorder is the mock recorder for MockSome2. +type MockSome2MockRecorder struct { + mock *MockSome2 +} + +// NewMockSome2 creates a new mock instance. +func NewMockSome2(ctrl *gomock.Controller) *MockSome2 { + mock := &MockSome2{ctrl: ctrl} + mock.recorder = &MockSome2MockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSome2) EXPECT() *MockSome2MockRecorder { + return m.recorder +} + +// Execute mocks base method. +func (m *MockSome2) Execute() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute") + ret0, _ := ret[0].(string) + return ret0 +} + +// Execute indicates an expected call of Execute. +func (mr *MockSome2MockRecorder) Execute() *MockSome2ExecuteCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockSome2)(nil).Execute)) + return &MockSome2ExecuteCall{Call: call} +} + +// MockSome2ExecuteCall wrap *gomock.Call +type MockSome2ExecuteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockSome2ExecuteCall) Return(arg0 string) *MockSome2ExecuteCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockSome2ExecuteCall) Do(f func() string) *MockSome2ExecuteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockSome2ExecuteCall) DoAndReturn(f func() string) *MockSome2ExecuteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// MockAlias is a mock of Alias interface. +type MockAlias struct { + ctrl *gomock.Controller + recorder *MockAliasMockRecorder + isgomock struct{} +} + +// MockAliasMockRecorder is the mock recorder for MockAlias. +type MockAliasMockRecorder struct { + mock *MockAlias +} + +// NewMockAlias creates a new mock instance. +func NewMockAlias(ctrl *gomock.Controller) *MockAlias { + mock := &MockAlias{ctrl: ctrl} + mock.recorder = &MockAliasMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAlias) EXPECT() *MockAliasMockRecorder { + return m.recorder +} + +// Execute mocks base method. +func (m *MockAlias) Execute() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute") + ret0, _ := ret[0].(string) + return ret0 +} + +// Execute indicates an expected call of Execute. +func (mr *MockAliasMockRecorder) Execute() *MockAliasExecuteCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockAlias)(nil).Execute)) + return &MockAliasExecuteCall{Call: call} +} + +// MockAliasExecuteCall wrap *gomock.Call +type MockAliasExecuteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockAliasExecuteCall) Return(arg0 string) *MockAliasExecuteCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockAliasExecuteCall) Do(f func() string) *MockAliasExecuteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockAliasExecuteCall) DoAndReturn(f func() string) *MockAliasExecuteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// MockGeneric is a mock of Generic interface. +type MockGeneric[T any] struct { + ctrl *gomock.Controller + recorder *MockGenericMockRecorder[T] + isgomock struct{} +} + +// MockGenericMockRecorder is the mock recorder for MockGeneric. +type MockGenericMockRecorder[T any] struct { + mock *MockGeneric[T] +} + +// NewMockGeneric creates a new mock instance. +func NewMockGeneric[T any](ctrl *gomock.Controller) *MockGeneric[T] { + mock := &MockGeneric[T]{ctrl: ctrl} + mock.recorder = &MockGenericMockRecorder[T]{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGeneric[T]) EXPECT() *MockGenericMockRecorder[T] { + return m.recorder +} + +// Execute mocks base method. +func (m *MockGeneric[T]) Execute(arg0 T) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute", arg0) + ret0, _ := ret[0].(string) + return ret0 +} + +// Execute indicates an expected call of Execute. +func (mr *MockGenericMockRecorder[T]) Execute(arg0 any) *MockGenericExecuteCall[T] { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockGeneric[T])(nil).Execute), arg0) + return &MockGenericExecuteCall[T]{Call: call} +} + +// MockGenericExecuteCall wrap *gomock.Call +type MockGenericExecuteCall[T any] struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockGenericExecuteCall[T]) Return(arg0 string) *MockGenericExecuteCall[T] { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockGenericExecuteCall[T]) Do(f func(T) string) *MockGenericExecuteCall[T] { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockGenericExecuteCall[T]) DoAndReturn(f func(T) string) *MockGenericExecuteCall[T] { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// MockIntGeneric is a mock of IntGeneric interface. +type MockIntGeneric struct { + ctrl *gomock.Controller + recorder *MockIntGenericMockRecorder + isgomock struct{} +} + +// MockIntGenericMockRecorder is the mock recorder for MockIntGeneric. +type MockIntGenericMockRecorder struct { + mock *MockIntGeneric +} + +// NewMockIntGeneric creates a new mock instance. +func NewMockIntGeneric(ctrl *gomock.Controller) *MockIntGeneric { + mock := &MockIntGeneric{ctrl: ctrl} + mock.recorder = &MockIntGenericMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIntGeneric) EXPECT() *MockIntGenericMockRecorder { + return m.recorder +} + +// Execute mocks base method. +func (m *MockIntGeneric) Execute(arg0 int) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute", arg0) + ret0, _ := ret[0].(string) + return ret0 +} + +// Execute indicates an expected call of Execute. +func (mr *MockIntGenericMockRecorder) Execute(arg0 any) *MockIntGenericExecuteCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockIntGeneric)(nil).Execute), arg0) + return &MockIntGenericExecuteCall{Call: call} +} + +// MockIntGenericExecuteCall wrap *gomock.Call +type MockIntGenericExecuteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIntGenericExecuteCall) Return(arg0 string) *MockIntGenericExecuteCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIntGenericExecuteCall) Do(f func(int) string) *MockIntGenericExecuteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIntGenericExecuteCall) DoAndReturn(f func(int) string) *MockIntGenericExecuteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// MockWithMethod is a mock of WithMethod interface. +type MockWithMethod struct { + ctrl *gomock.Controller + recorder *MockWithMethodMockRecorder + isgomock struct{} +} + +// MockWithMethodMockRecorder is the mock recorder for MockWithMethod. +type MockWithMethodMockRecorder struct { + mock *MockWithMethod +} + +// NewMockWithMethod creates a new mock instance. +func NewMockWithMethod(ctrl *gomock.Controller) *MockWithMethod { + mock := &MockWithMethod{ctrl: ctrl} + mock.recorder = &MockWithMethodMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockWithMethod) EXPECT() *MockWithMethodMockRecorder { + return m.recorder +} + +// Execute mocks base method. +func (m *MockWithMethod) Execute(arg0 string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute", arg0) + ret0, _ := ret[0].(string) + return ret0 +} + +// Execute indicates an expected call of Execute. +func (mr *MockWithMethodMockRecorder) Execute(arg0 any) *MockWithMethodExecuteCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockWithMethod)(nil).Execute), arg0) + return &MockWithMethodExecuteCall{Call: call} +} + +// MockWithMethodExecuteCall wrap *gomock.Call +type MockWithMethodExecuteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockWithMethodExecuteCall) Return(arg0 string) *MockWithMethodExecuteCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockWithMethodExecuteCall) Do(f func(string) string) *MockWithMethodExecuteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockWithMethodExecuteCall) DoAndReturn(f func(string) string) *MockWithMethodExecuteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// MockIntIter is a mock of IntIter interface. +type MockIntIter struct { + ctrl *gomock.Controller + recorder *MockIntIterMockRecorder + isgomock struct{} +} + +// MockIntIterMockRecorder is the mock recorder for MockIntIter. +type MockIntIterMockRecorder struct { + mock *MockIntIter +} + +// NewMockIntIter creates a new mock instance. +func NewMockIntIter(ctrl *gomock.Controller) *MockIntIter { + mock := &MockIntIter{ctrl: ctrl} + mock.recorder = &MockIntIterMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIntIter) EXPECT() *MockIntIterMockRecorder { + return m.recorder +} + +// Execute mocks base method. +func (m *MockIntIter) Execute(yield func(int) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Execute", yield) +} + +// Execute indicates an expected call of Execute. +func (mr *MockIntIterMockRecorder) Execute(yield any) *MockIntIterExecuteCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockIntIter)(nil).Execute), yield) + return &MockIntIterExecuteCall{Call: call} +} + +// MockIntIterExecuteCall wrap *gomock.Call +type MockIntIterExecuteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIntIterExecuteCall) Return() *MockIntIterExecuteCall { + c.Call = c.Call.Return() + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIntIterExecuteCall) Do(f func(func(int) bool)) *MockIntIterExecuteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIntIterExecuteCall) DoAndReturn(f func(func(int) bool)) *MockIntIterExecuteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/mockgen/internal/tests/signature/signatures.go b/mockgen/internal/tests/signature/signatures.go new file mode 100644 index 0000000..538b7ee --- /dev/null +++ b/mockgen/internal/tests/signature/signatures.go @@ -0,0 +1,25 @@ +package signature + +import ( + "iter" +) + +//go:generate mockgen -typed -destination=source_mode/mock.go -package=source_mode -source=signatures.go +//go:generate mockgen -typed -destination=package_mode/mock.go -package=package_mode . Some,Some2,Alias,Generic,IntGeneric,WithMethod,IntIter +//go:generate mockgen -typed -destination=package_mode/external_mock.go -package=package_mode iter Seq,Seq2 + +type Some func() string + +type Some2 Some + +type Alias = Some + +type Generic[T any] func(T) string + +type IntGeneric Generic[int] + +type WithMethod func(string) string + +func (f WithMethod) Method() {} + +type IntIter iter.Seq[int] diff --git a/mockgen/internal/tests/signature/source_mode/mock.go b/mockgen/internal/tests/signature/source_mode/mock.go new file mode 100644 index 0000000..79106ef --- /dev/null +++ b/mockgen/internal/tests/signature/source_mode/mock.go @@ -0,0 +1,202 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: signatures.go +// +// Generated by this command: +// +// ___funtions_source_mode -typed -destination=source_mode/mock.go -package=source_mode -source=signatures.go +// + +// Package source_mode is a generated GoMock package. +package source_mode + +import ( + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockSome is a mock of Some interface. +type MockSome struct { + ctrl *gomock.Controller + recorder *MockSomeMockRecorder + isgomock struct{} +} + +// MockSomeMockRecorder is the mock recorder for MockSome. +type MockSomeMockRecorder struct { + mock *MockSome +} + +// NewMockSome creates a new mock instance. +func NewMockSome(ctrl *gomock.Controller) *MockSome { + mock := &MockSome{ctrl: ctrl} + mock.recorder = &MockSomeMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockSome) EXPECT() *MockSomeMockRecorder { + return m.recorder +} + +// Execute mocks base method. +func (m *MockSome) Execute() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute") + ret0, _ := ret[0].(string) + return ret0 +} + +// Execute indicates an expected call of Execute. +func (mr *MockSomeMockRecorder) Execute() *MockSomeExecuteCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockSome)(nil).Execute)) + return &MockSomeExecuteCall{Call: call} +} + +// MockSomeExecuteCall wrap *gomock.Call +type MockSomeExecuteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockSomeExecuteCall) Return(arg0 string) *MockSomeExecuteCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockSomeExecuteCall) Do(f func() string) *MockSomeExecuteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockSomeExecuteCall) DoAndReturn(f func() string) *MockSomeExecuteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// MockGeneric is a mock of Generic interface. +type MockGeneric[T any] struct { + ctrl *gomock.Controller + recorder *MockGenericMockRecorder[T] + isgomock struct{} +} + +// MockGenericMockRecorder is the mock recorder for MockGeneric. +type MockGenericMockRecorder[T any] struct { + mock *MockGeneric[T] +} + +// NewMockGeneric creates a new mock instance. +func NewMockGeneric[T any](ctrl *gomock.Controller) *MockGeneric[T] { + mock := &MockGeneric[T]{ctrl: ctrl} + mock.recorder = &MockGenericMockRecorder[T]{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGeneric[T]) EXPECT() *MockGenericMockRecorder[T] { + return m.recorder +} + +// Execute mocks base method. +func (m *MockGeneric[T]) Execute(arg0 T) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute", arg0) + ret0, _ := ret[0].(string) + return ret0 +} + +// Execute indicates an expected call of Execute. +func (mr *MockGenericMockRecorder[T]) Execute(arg0 any) *MockGenericExecuteCall[T] { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockGeneric[T])(nil).Execute), arg0) + return &MockGenericExecuteCall[T]{Call: call} +} + +// MockGenericExecuteCall wrap *gomock.Call +type MockGenericExecuteCall[T any] struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockGenericExecuteCall[T]) Return(arg0 string) *MockGenericExecuteCall[T] { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockGenericExecuteCall[T]) Do(f func(T) string) *MockGenericExecuteCall[T] { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockGenericExecuteCall[T]) DoAndReturn(f func(T) string) *MockGenericExecuteCall[T] { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// MockWithMethod is a mock of WithMethod interface. +type MockWithMethod struct { + ctrl *gomock.Controller + recorder *MockWithMethodMockRecorder + isgomock struct{} +} + +// MockWithMethodMockRecorder is the mock recorder for MockWithMethod. +type MockWithMethodMockRecorder struct { + mock *MockWithMethod +} + +// NewMockWithMethod creates a new mock instance. +func NewMockWithMethod(ctrl *gomock.Controller) *MockWithMethod { + mock := &MockWithMethod{ctrl: ctrl} + mock.recorder = &MockWithMethodMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockWithMethod) EXPECT() *MockWithMethodMockRecorder { + return m.recorder +} + +// Execute mocks base method. +func (m *MockWithMethod) Execute(arg0 string) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Execute", arg0) + ret0, _ := ret[0].(string) + return ret0 +} + +// Execute indicates an expected call of Execute. +func (mr *MockWithMethodMockRecorder) Execute(arg0 any) *MockWithMethodExecuteCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockWithMethod)(nil).Execute), arg0) + return &MockWithMethodExecuteCall{Call: call} +} + +// MockWithMethodExecuteCall wrap *gomock.Call +type MockWithMethodExecuteCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockWithMethodExecuteCall) Return(arg0 string) *MockWithMethodExecuteCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockWithMethodExecuteCall) Do(f func(string) string) *MockWithMethodExecuteCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockWithMethodExecuteCall) DoAndReturn(f func(string) string) *MockWithMethodExecuteCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/mockgen/package_mode.go b/mockgen/package_mode.go index bce27c1..eb4b277 100644 --- a/mockgen/package_mode.go +++ b/mockgen/package_mode.go @@ -87,34 +87,49 @@ func parseInterface(obj types.Object) (*model.Interface, error) { return nil, fmt.Errorf("%s is not an interface. it is a %T", obj.Name(), obj.Type().Underlying()) } - iface, ok := named.Underlying().(*types.Interface) - if !ok { - return nil, fmt.Errorf("%s is not an interface. it is a %T", obj.Name(), obj.Type().Underlying()) - } + var methods []*model.Method + switch t := obj.Type().Underlying().(type) { + case *types.Interface: + if isConstraint(t) { + return nil, fmt.Errorf("interface %s is a constraint", obj.Name()) + } - if isConstraint(iface) { - return nil, fmt.Errorf("interface %s is a constraint", obj.Name()) - } + methods = make([]*model.Method, t.NumMethods()) + for i := range t.NumMethods() { + method := t.Method(i) + typedMethod, ok := method.Type().(*types.Signature) + if !ok { + return nil, fmt.Errorf("method %s is not a signature", method.Name()) + } - methods := make([]*model.Method, iface.NumMethods()) - for i := range iface.NumMethods() { - method := iface.Method(i) - typedMethod, ok := method.Type().(*types.Signature) - if !ok { - return nil, fmt.Errorf("method %s is not a signature", method.Name()) - } + modelFunc, err := parseFunc(typedMethod) + if err != nil { + return nil, newParseTypeError("parse method", typedMethod.String(), err) + } - modelFunc, err := parseFunc(typedMethod) + methods[i] = &model.Method{ + Name: method.Name(), + In: modelFunc.In, + Out: modelFunc.Out, + Variadic: modelFunc.Variadic, + } + } + case *types.Signature: + method, err := parseFunc(t) if err != nil { - return nil, newParseTypeError("parse method", typedMethod.String(), err) + return nil, fmt.Errorf("parse signature %s: %w", obj.Name(), err) } - methods[i] = &model.Method{ - Name: method.Name(), - In: modelFunc.In, - Out: modelFunc.Out, - Variadic: modelFunc.Variadic, - } + methods = append(methods, + &model.Method{ + Name: "Execute", + In: method.In, + Out: method.Out, + Variadic: method.Variadic, + }, + ) + default: + return nil, fmt.Errorf("%s is not an interface or a signature. it is a %T", obj.Name(), obj.Type().Underlying()) } if named.TypeParams() == nil { diff --git a/mockgen/parse.go b/mockgen/parse.go index da0cc5e..3f89fbc 100644 --- a/mockgen/parse.go +++ b/mockgen/parse.go @@ -268,6 +268,29 @@ func (p *fileParser) parseFile(importPath string, file *ast.File) (*model.Packag delete(p.includeNamesSet, name) } + + for signature := range iterSignatures(file) { + name := signature.name.String() + if _, ok := p.excludeNamesSet[name]; ok { + continue + } + + // All signatures are included if no filter was specified. + if len(p.includeNamesSet) > 0 { + if _, ok := p.includeNamesSet[name]; !ok { + continue + } + } + + i, err := p.parseSignature(name, importPath, signature) + if err != nil { + return nil, fmt.Errorf("parsing signature %s: %w", name, err) + } + + is = append(is, i) + delete(p.includeNamesSet, name) + } + return &model.Package{ Name: file.Name.String(), PkgPath: importPath, @@ -352,6 +375,21 @@ func (p *fileParser) constructTps(it *namedInterface) (tps map[string]model.Type return tps } +func (p *fileParser) constructTpsForSignature(signature *namedSignature) (tps map[string]model.Type) { + tps = make(map[string]model.Type) + n := 0 + for _, tp := range signature.typeParams { + for _, tm := range tp.Names { + tps[tm.Name] = nil + if len(signature.instTypes) != 0 { + tps[tm.Name] = signature.instTypes[n] + n++ + } + } + } + return tps +} + // parseInterface loads interface specified by pkg and name, parses it and returns // a new model with the parsed. func (p *fileParser) parseInterface(name, pkg string, it *namedInterface) (*model.Interface, error) { @@ -491,6 +529,33 @@ func (p *fileParser) parseMethod(field *ast.Field, it *namedInterface, iface *mo } } +// parseSignature parses the signature and returns it as an interface with the Execute method +func (p *fileParser) parseSignature(name, pkg string, signature *namedSignature) (*model.Interface, error) { + tps := p.constructTpsForSignature(signature) + typeParams, err := p.parseFieldList(pkg, signature.typeParams, tps) + if err != nil { + return nil, fmt.Errorf("parsing type params: %w", err) + } + + in, variadic, out, err := p.parseFunc(pkg, signature.funcType, tps) + if err != nil { + return nil, fmt.Errorf("func parsing: %w", err) + } + + return &model.Interface{ + Name: name, + Methods: []*model.Method{ + { + Name: "Execute", + In: in, + Out: out, + Variadic: variadic, + }, + }, + TypeParams: typeParams, + }, nil +} + func (p *fileParser) parseFunc(pkg string, f *ast.FuncType, tps map[string]model.Type) (inParam []*model.Parameter, variadic *model.Parameter, outParam []*model.Parameter, err error) { if f.Params != nil { regParams := f.Params.List @@ -795,6 +860,41 @@ func iterInterfaces(file *ast.File) <-chan *namedInterface { return ch } +type namedSignature struct { + name *ast.Ident + funcType *ast.FuncType + typeParams []*ast.Field + embeddedInstTypeParams []ast.Expr + instTypes []model.Type +} + +func iterSignatures(file *ast.File) <-chan *namedSignature { + ch := make(chan *namedSignature) + go func() { + for _, decl := range file.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.TYPE { + continue + } + for _, spec := range gd.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + + funcType, ok := ts.Type.(*ast.FuncType) + if !ok { + continue + } + + ch <- &namedSignature{name: ts.Name, funcType: funcType, typeParams: getTypeSpecTypeParams(ts)} + } + } + close(ch) + }() + return ch +} + // isVariadic returns whether the function is variadic. func isVariadic(f *ast.FuncType) bool { nargs := len(f.Params.List) From 5d7d0ad1dae2e7541382b246b0470368da38743b Mon Sep 17 00:00:00 2001 From: Tulskiy Aleksandr Date: Fri, 3 Oct 2025 12:50:47 +0300 Subject: [PATCH 2/2] Regenerated mocks --- mockgen/internal/tests/signature/package_mode/external_mock.go | 2 +- mockgen/internal/tests/signature/package_mode/mock.go | 2 +- mockgen/internal/tests/signature/source_mode/mock.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mockgen/internal/tests/signature/package_mode/external_mock.go b/mockgen/internal/tests/signature/package_mode/external_mock.go index 7a1bc7f..690b2f1 100644 --- a/mockgen/internal/tests/signature/package_mode/external_mock.go +++ b/mockgen/internal/tests/signature/package_mode/external_mock.go @@ -3,7 +3,7 @@ // // Generated by this command: // -// ___funcitons_package_mode_external -typed -destination=package_mode/external_mock.go -package=package_mode iter Seq,Seq2 +// mockgen -typed -destination=package_mode/external_mock.go -package=package_mode iter Seq,Seq2 // // Package package_mode is a generated GoMock package. diff --git a/mockgen/internal/tests/signature/package_mode/mock.go b/mockgen/internal/tests/signature/package_mode/mock.go index 225f718..39a6ac9 100644 --- a/mockgen/internal/tests/signature/package_mode/mock.go +++ b/mockgen/internal/tests/signature/package_mode/mock.go @@ -3,7 +3,7 @@ // // Generated by this command: // -// ___funtions_package_mode -typed -destination=package_mode/mock.go -package=package_mode . Some,Some2,Alias,Generic,IntGeneric,WithMethod,IntIter +// mockgen -typed -destination=package_mode/mock.go -package=package_mode . Some,Some2,Alias,Generic,IntGeneric,WithMethod,IntIter // // Package package_mode is a generated GoMock package. diff --git a/mockgen/internal/tests/signature/source_mode/mock.go b/mockgen/internal/tests/signature/source_mode/mock.go index 79106ef..e3b81bf 100644 --- a/mockgen/internal/tests/signature/source_mode/mock.go +++ b/mockgen/internal/tests/signature/source_mode/mock.go @@ -3,7 +3,7 @@ // // Generated by this command: // -// ___funtions_source_mode -typed -destination=source_mode/mock.go -package=source_mode -source=signatures.go +// mockgen -typed -destination=source_mode/mock.go -package=source_mode -source=signatures.go // // Package source_mode is a generated GoMock package.