Skip to content
This repository has been archived by the owner on Jun 27, 2023. It is now read-only.

Nested interfaces with Generics #643

Open
bradleygore opened this issue May 12, 2022 · 6 comments
Open

Nested interfaces with Generics #643

bradleygore opened this issue May 12, 2022 · 6 comments
Milestone

Comments

@bradleygore
Copy link

Actual behavior

Typing for nested generic interface is scoped to package the interface belongs to and not the pkgs the type belongs to.

Here is a complete example: https://github.com/bradleygore/gomock-generics-issue/blob/master/workers/custom.go

I pulled down gomock repo into ~/git/gomock and checked out master and did cd mockgen && go install ./... to install it.

I then updated the go.mod to path to that resource:

replace github.com/golang/mock => /Users/bgore/git/gomock

I then ran the cmd to generate mocks against the dev branch:

go version go1.18.1 darwin/amd64

~/git/gomock$ git rev-parse HEAD
73266f9366fcf2ccef0b880618e5a9266e4136f4

// running mockgen -version from my issue repo
~/git/gomock-generics-issue/workers$ mockgen -version
(devel)

// running mockgen to generate a mock from my issue repo
~/git/gomock-generics/issue/workers$ mockgen -destination=./mocks/CustomWorker.go -package=mock_iface gomock-generics-issue/workers CustomWorker

This did not produce any error, but it did produce an issue in the output:


// The CustomWorkDetail type is defined in `workers` pkg, not in `iface` pkg. However, `WorkResult` is defined in `iface` pkg.
// It seems that non-primitive type declarations for nested generic interface are presumed to be from same pkg as generic interface.

func (m *MockCustomWorker) DoWork(arg0 ...interface{}) iface.WorkResult[iface.CustomWorkDetail] {
	m.ctrl.T.Helper()
	varargs := []interface{}{}
	for _, a := range arg0 {
		varargs = append(varargs, a)
	}
	ret := m.ctrl.Call(m, "DoWork", varargs...)
	ret0, _ := ret[0].(iface.WorkResult[iface.CustomWorkDetail])
	return ret0
}

Expected behavior

Expected behavior would be that the type for the generic parameter is not presumed to come from the same pkg as the generic interface's definition, as this causes the mock to not compile.

In this specific scenario, the mock would import workers pkg, and the output of the above would look like this:

// import path of workers pkg: "gomock-generics-issue/workers"

func (m *MockCustomWorker) DoWork(arg0 ...interface{}) iface.WorkResult[workers.CustomWorkDetail] {
	m.ctrl.T.Helper()
	varargs := []interface{}{}
	for _, a := range arg0 {
		varargs = append(varargs, a)
	}
	ret := m.ctrl.Call(m, "DoWork", varargs...)
	ret0, _ := ret[0].(iface.WorkResult[workers.CustomWorkDetail])
	return ret0
}

Additional Information

My primary use-case for this is a Repository style setup. Consider:

// defined in pkg `repos`
type BaseRepo interface[T any] {
  Get(filters *Filters) (*PagedResults[T], error)
  GetByID(int64) (T, error)
  Create(T) error
  Update(T) error
  Delete(T) error
}

// defined in pkg `repos`
type UserRepo interface {
  // User is defined in pkg `models`
  BaseRepo[models.User]
  FindByEmail(string email) (*models.User, error)
}

Notes for the Maintainers

🎉 Y'all are awesome and gomock is fantastic! 🎉

@codyoss
Copy link
Member

codyoss commented Jul 8, 2022

@bradleygore note as written this would not work ever since generics can only be used in source mode currently, reflection is not supported.

@bradleygore
Copy link
Author

Hey @codyoss - so are you saying that if I ran it as mockgen -source=./custom.go -destination=./mocks/CustomWorker.go CustomWorker it would work? I will try it this way soon and report back - thanks!!

@codyoss
Copy link
Member

codyoss commented Jul 8, 2022

@bradleygore it does not, but just wanted to be clear with expectations that reflect mode will never work with generics.(At least for the foreseeable future)

@bradleygore
Copy link
Author

Ah, ok - I see. Thanks for the explanation 😄

@bradleygore
Copy link
Author

Hey @codyoss - my team and I love gomock so I wanted to try to contribute back. I took a stab at helping with this and it seems to work nicely (I think... 😅 ). #663

There is a bot on it stating I need to sign a Contributor's License Agreement - so I just did that.

I really hope this is helpful, and am looking forward to feedback!

Thank you!

@danlaine
Copy link

+1 for fixing this please 🙏

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants