mock icon indicating copy to clipboard operation
mock copied to clipboard

Interfaces that are type aliases are ignored

Open ash2k opened this issue 1 year ago • 5 comments

Actual behavior

I'm trying to generate a mock for a type that is a type alias for a generic interface. This is new functionality in gRPC (https://github.com/grpc/grpc-go/issues/7030), available in protoc-gen-go-grpc v1.5.0.

Before v1.5.0 the type looks like this (works fine with source and reflect modes):

type GitLabFlux_ReconcileProjectsClient interface {
	Recv() (*ReconcileProjectsResponse, error)
	grpc.ClientStream
}

v1.5.1 generates the following:

// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type GitLabFlux_ReconcileProjectsClient = grpc.ServerStreamingClient[ReconcileProjectsResponse]

gRPC type looks like this:

// ServerStreamingClient represents the client side of a server-streaming (one
// request, many responses) RPC. It is generic over the type of the response
// message. It is used in generated code.
type ServerStreamingClient[Res any] interface {
	Recv() (*Res, error)
	ClientStream
}

mockgen just ignores the type as if it's not in the file.

I've also tried to add a replacement type like this:

// Doesn't work
type gitLabFlux_ReconcileProjectsClient interface {
	rpc.GitLabFlux_ReconcileProjectsClient
}

The above produces an error (I couldn't fix it with -aux_files):

2024/07/30 19:21:56 Loading input failed: doc.go:16:2: unknown embedded interface gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/module/flux/rpc.GitLabFlux_ReconcileProjectsClient

If I use the gRPC's interface explicitly, it works fine:

// Works fine. This is my own type, not the generated one.
type gitLabFlux_ReconcileProjectsClient interface {
	grpc.ServerStreamingClient[rpc.ReconcileProjectsResponse]
}

Expected behavior

I expect the generated alias GitLabFlux_ReconcileProjectsClient interface to generate a mock. I generate mocks for all gRPC-generated interfaces but this one (and similar ones) is skipped so I have to work this around, which is annoying.

To Reproduce

Use a type alias and try to generate a mock for it.

Additional Information

  • gomock mode (reflect or source): source
  • gomock version or git ref: v0.4.0
  • golang version: v1.22.5

Triage Notes for the Maintainers

ash2k avatar Jul 30 '24 09:07 ash2k

Here is a minimal reproducible form

package compliance

import "go.uber.org/mock/gomock"

type X = T[gomock.Call]

type T[t any] interface {
}

//go:generate mockgen . A
type A interface {
	A(X) X
}

It generates invalid go code

func (m *MockA) A(arg0 compliance.T[go.uber.org/mock/gomock.Call]) compliance.T[go.uber.org/mock/gomock.Call] {
Failed to format generated source code: mocks/a.go:42:37: expected type, found 'go' (and 1 more errors)

janisz avatar Aug 01 '24 13:08 janisz

https://github.com/uber-go/mock/blob/857e269dd5c375135eb9ae65f382ad7c58eaa4b8/mockgen/model/model.go#L420

This is a problematic line. As aliases are "invisible" during runtime in reflect mode we get an underlying type instead of alias this results in fully qualified path in the generic. This may be solved if we store all imports for a type and then sanitize type

https://github.com/golang/proposal/blob/master/design/18130-type-alias.md#reflect

janisz avatar Aug 05 '24 17:08 janisz

I think the fix in reflect mode is via the new API, see https://github.com/uber-go/mock/issues/186#issuecomment-2259706052. I'm using source mode, presumably the fix is different.

ash2k avatar Aug 06 '24 00:08 ash2k

Same issue with grpc stream interface (which is generic now). This is a blocking issue failing tests in CI.

pereslava avatar Aug 07 '24 18:08 pereslava

It worked for me in source mode on generated from .proto file grpc streaming inetrface

pereslava avatar Aug 08 '24 15:08 pereslava

Hi, I'm not sure if this issue is actually resolved. When using gomock v0.5.0 in source mode, type aliases is still ignored. I believe the fix #207 doesn't apply for source mode

oumichae1 avatar Nov 22 '24 20:11 oumichae1

I'm just trying to make it work with 0.5.1 and it seems it does not. Input types:

From grpc-go:

type ClientStream interface {
	// methods here
}

type ServerStreamingClient[Res any] interface {
	Recv() (*Res, error)
	ClientStream
}

Generated gRPC plumbing:

type ReconcileProjectsResponse struct {
	// Fields
}

type GitLabFlux_ReconcileProjectsClient = grpc.ServerStreamingClient[ReconcileProjectsResponse]

Command:

go run go.uber.org/mock/[email protected] -typed -destination "mock_rpc_for_test.go" -package "agent" "gitlab.com/gitlab-org/cluster-integration/gitlab-agent/v17/internal/module/flux/rpc" "GitLabFlux_ReconcileProjectsClient,GitLabFluxClient"

Invalid code from the generated mock:

func (m *MockGitLabFluxClient) ReconcileProjects(ctx context.Context, in *rpc.ReconcileProjectsRequest, opts ...grpc.CallOption) (rpc.GitLabFlux_ReconcileProjectsClient[rpc.ReconcileProjectsResponse], error) {

Note that the alias type rpc.GitLabFlux_ReconcileProjectsClient is parametrized with a type parameter. This is incorrect, it does not have a type parameter.

@JacobOaks Could you reopen this issue as it's not fixed?

ash2k avatar Apr 09 '25 12:04 ash2k