From e16baf650d37c01cf146c09e6f3dfcfe2bf49876 Mon Sep 17 00:00:00 2001 From: Micah Date: Tue, 19 Sep 2023 20:28:00 +0900 Subject: [PATCH 1/4] feat: ignore interfaces that contains type constraints --- mockgen/generic_go118.go | 14 ++++ mockgen/internal/tests/generics/generics.go | 8 +++ .../generics/source/mock_generics_mock.go | 66 ++++++++++++------- mockgen/parse.go | 3 + 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/mockgen/generic_go118.go b/mockgen/generic_go118.go index 635402d..73d00b6 100644 --- a/mockgen/generic_go118.go +++ b/mockgen/generic_go118.go @@ -11,8 +11,10 @@ package main import ( + "errors" "fmt" "go/ast" + "go/token" "strings" "go.uber.org/mock/mockgen/model" @@ -98,6 +100,16 @@ func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, if case *ast.IndexListExpr: indices = v.Indices typ = v.X + case *ast.UnaryExpr: + if v.Op == token.TILDE { + return nil, errIgnore + } + return nil, fmt.Errorf("~T may only appear as constraint for %T", field.Type) + case *ast.BinaryExpr: + if v.Op == token.OR { + return nil, errIgnore + } + return nil, fmt.Errorf("A|B may only appear as constraint for %T", field.Type) default: return nil, fmt.Errorf("don't know how to mock method of type %T", field.Type) } @@ -114,3 +126,5 @@ func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, if return p.parseMethod(nf, it, iface, pkg, tps) } + +var errIgnore = errors.New("parser decided to ignore the interface for constraints") diff --git a/mockgen/internal/tests/generics/generics.go b/mockgen/internal/tests/generics/generics.go index 85d1f0d..db0c49e 100644 --- a/mockgen/internal/tests/generics/generics.go +++ b/mockgen/internal/tests/generics/generics.go @@ -57,3 +57,11 @@ type SolarSystem[T constraints.Ordered] interface { type Earth[R any] interface { Water(R) []R } + +type Water[R any, C UnsignedInteger] interface { + Fish(R) []C +} + +type UnsignedInteger interface { + ~uint | ~uint32 | ~uint64 +} diff --git a/mockgen/internal/tests/generics/source/mock_generics_mock.go b/mockgen/internal/tests/generics/source/mock_generics_mock.go index 08c1e00..d92a8d4 100644 --- a/mockgen/internal/tests/generics/source/mock_generics_mock.go +++ b/mockgen/internal/tests/generics/source/mock_generics_mock.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: generics.go - +// +// Generated by this command: +// +// mockgen --source=generics.go --destination=source/mock_generics_mock.go --package source +// // Package source is a generated GoMock package. package source @@ -306,29 +310,6 @@ func (mr *MockBarMockRecorder[T, R]) Two(arg0 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Two", reflect.TypeOf((*MockBar[T, R])(nil).Two), arg0) } -// MockIface is a mock of Iface interface. -type MockIface[T any] struct { - ctrl *gomock.Controller - recorder *MockIfaceMockRecorder[T] -} - -// MockIfaceMockRecorder is the mock recorder for MockIface. -type MockIfaceMockRecorder[T any] struct { - mock *MockIface[T] -} - -// NewMockIface creates a new mock instance. -func NewMockIface[T any](ctrl *gomock.Controller) *MockIface[T] { - mock := &MockIface[T]{ctrl: ctrl} - mock.recorder = &MockIfaceMockRecorder[T]{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockIface[T]) EXPECT() *MockIfaceMockRecorder[T] { - return m.recorder -} - // MockUniverse is a mock of Universe interface. type MockUniverse[T constraints.Signed] struct { ctrl *gomock.Controller @@ -476,3 +457,40 @@ func (mr *MockEarthMockRecorder[R]) Water(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Water", reflect.TypeOf((*MockEarth[R])(nil).Water), arg0) } + +// MockWater is a mock of Water interface. +type MockWater[R any, C generics.UnsignedInteger] struct { + ctrl *gomock.Controller + recorder *MockWaterMockRecorder[R, C] +} + +// MockWaterMockRecorder is the mock recorder for MockWater. +type MockWaterMockRecorder[R any, C generics.UnsignedInteger] struct { + mock *MockWater[R, C] +} + +// NewMockWater creates a new mock instance. +func NewMockWater[R any, C generics.UnsignedInteger](ctrl *gomock.Controller) *MockWater[R, C] { + mock := &MockWater[R, C]{ctrl: ctrl} + mock.recorder = &MockWaterMockRecorder[R, C]{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockWater[R, C]) EXPECT() *MockWaterMockRecorder[R, C] { + return m.recorder +} + +// Fish mocks base method. +func (m *MockWater[R, C]) Fish(arg0 R) []C { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Fish", arg0) + ret0, _ := ret[0].([]C) + return ret0 +} + +// Fish indicates an expected call of Fish. +func (mr *MockWaterMockRecorder[R, C]) Fish(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fish", reflect.TypeOf((*MockWater[R, C])(nil).Fish), arg0) +} diff --git a/mockgen/parse.go b/mockgen/parse.go index 9521409..cc36039 100644 --- a/mockgen/parse.go +++ b/mockgen/parse.go @@ -232,6 +232,9 @@ func (p *fileParser) parseFile(importPath string, file *ast.File) (*model.Packag continue } i, err := p.parseInterface(ni.name.String(), importPath, ni) + if err == errIgnore { + continue + } if err != nil { return nil, err } From d7179e545cadb1a3a659fef7da48e28092dc554b Mon Sep 17 00:00:00 2001 From: "Hoonmin Kim (Micah)" Date: Sun, 24 Sep 2023 23:50:50 +0000 Subject: [PATCH 2/4] Update mockgen/generic_go118.go Co-authored-by: Jacob Oaks --- mockgen/generic_go118.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mockgen/generic_go118.go b/mockgen/generic_go118.go index 73d00b6..d81357f 100644 --- a/mockgen/generic_go118.go +++ b/mockgen/generic_go118.go @@ -127,4 +127,4 @@ func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, if return p.parseMethod(nf, it, iface, pkg, tps) } -var errIgnore = errors.New("parser decided to ignore the interface for constraints") +var errConstraintInterface = errors.New("interface contains constraints") From 32ec09c03efccec15e4ff479e3bf363ae2216bfb Mon Sep 17 00:00:00 2001 From: "Hoonmin Kim (Micah)" Date: Sun, 24 Sep 2023 23:51:01 +0000 Subject: [PATCH 3/4] Update mockgen/parse.go Co-authored-by: Jacob Oaks --- mockgen/parse.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mockgen/parse.go b/mockgen/parse.go index cc36039..f43321c 100644 --- a/mockgen/parse.go +++ b/mockgen/parse.go @@ -232,7 +232,7 @@ func (p *fileParser) parseFile(importPath string, file *ast.File) (*model.Packag continue } i, err := p.parseInterface(ni.name.String(), importPath, ni) - if err == errIgnore { + if errors.Is(err, errConstraintInterface) { continue } if err != nil { From 56f1d744403b9f45dd07c6d130295f41747fbb96 Mon Sep 17 00:00:00 2001 From: Micah Date: Mon, 25 Sep 2023 08:58:24 +0900 Subject: [PATCH 4/4] Use errConstraintInterface --- mockgen/generic_go118.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mockgen/generic_go118.go b/mockgen/generic_go118.go index d81357f..b7b4494 100644 --- a/mockgen/generic_go118.go +++ b/mockgen/generic_go118.go @@ -102,12 +102,12 @@ func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, if typ = v.X case *ast.UnaryExpr: if v.Op == token.TILDE { - return nil, errIgnore + return nil, errConstraintInterface } return nil, fmt.Errorf("~T may only appear as constraint for %T", field.Type) case *ast.BinaryExpr: if v.Op == token.OR { - return nil, errIgnore + return nil, errConstraintInterface } return nil, fmt.Errorf("A|B may only appear as constraint for %T", field.Type) default: