Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should not resolve type alias when generate mock files #216

Open
lance6716 opened this issue Oct 21, 2024 · 2 comments
Open

Should not resolve type alias when generate mock files #216

lance6716 opened this issue Oct 21, 2024 · 2 comments
Assignees

Comments

@lance6716
Copy link

lance6716 commented Oct 21, 2024

Actual behavior A clear and concise description of what the bug is.

we have an interface that uses "github.com/tikv/client-go/v2/tikv".Codec. And that type is defined as

under "github.com/tikv/client-go/v2/tikv":

import "github.com/tikv/client-go/v2/internal/apicodec"
...
type Codec = apicodec.Codec

In the generated mock files, it directly imports "github.com/tikv/client-go/v2/internal/apicodec" which is not allowed for the non-internal usage.

Expected behavior A clear and concise description of what you expected to
happen.

It should import "github.com/tikv/client-go/v2/tikv" instead

To Reproduce Steps to reproduce the behavior

  1. ...
  2. ...

Additional Information

  • gomock mode (reflect or source): default value
  • gomock version or git ref: v0.5.0
  • golang version: 1.23

Triage Notes for the Maintainers

@JacobOaks
Copy link
Contributor

Hey @lance6716 - how is the Codec type being used in terms of the interface you're trying to mock? Can you show us:

  • The definition of the interface you're trying to mock
  • The mockgen command you're running

Thanks

@lance6716
Copy link
Author

lance6716 commented Oct 22, 2024

@JacobOaks I pushed example code here https://github.com/lance6716/mock/tree/show216/example

The command is mockgen -package mock . Foo > ./mock/gen.go

And can you take a look at #171 ? Thanks.

@JacobOaks JacobOaks self-assigned this Oct 22, 2024
JacobOaks added a commit to JacobOaks/mock that referenced this issue Oct 24, 2024
v0.5.0 included uber-go#207, which replaced reflect mode with package mode.
One issue with package mode that came up (ref: uber-go#216) was that
generated mocks for interfaces that referred to alias types
were referring to the aliases' underlying names instead.

e.g.,
source:
```go
import "github.com/tikv/client-go/v2/internal/apicodec"
...
type Codec = apicodec.Codec

type Foo interface{
	Bar() Codec
}
```
mock:
```go
func (m *MockFoo) Bar() apicodec.Codec { // This is a problem, since apicodec is an internal package.
    // ...
}
```

While technically this problem is solved in Go 1.23 with explicit alias types representation,
(indeed, if you run mockgen on the example in the linked issue with
`GODEBUG=gotypesalias=1`, you get the expected behavior)
since we support the last two versions, we can't bump `go.mod` to 1.23 yet.
This leaves us with the old behavior, where `go/types` does not track alias types.
You can tell if an object is an alias, but not a type itself,
and there is no way to retrieve the object of interest
at the point where we are recursively parsing method types.

This PR works around this issue (temporarily) by using syntax information
to find all references to aliases in the source package.
When we find one, we record it in a mapping of underlying type -> alias name.
Later, while we parse the type tree, we replace any underlying types
in the mapping with their alias names.

The unexpected side effect of this is that _all_ references to the underlying type
in the generated mocks will be replaced with the alias, even if
the source used the underlying name. This is fine because:
* If the alias is in the mapping, it was used at least once, which means its accessible.
* From a type-checking perspective, aliases and their underlying types are equivalent.

With this PR, the mocks get generated correctly now:
```go
func (m *MockFoo) Bar() Codec {
    // ...
}
```

Once we can bump `go.mod` to 1.23, we should definitely remove this,
since the new type alias type nodes solve this problem automatically.
JacobOaks added a commit that referenced this issue Oct 28, 2024
v0.5.0 included #207, which replaced reflect mode with package mode. One
issue with package mode that came up (ref: #216) was that generated
mocks for interfaces that referred to alias types were referring to the
aliases' underlying names instead.

e.g.,
some package:
```go
package somgpkg

import "somepkg/internal/apicodec"
...
type Codec = apicodec.Codec
```

mockgen input:
```go
type Foo interface{
	Bar() somepkg.Codec
}
```
mock:
```go
func (m *MockFoo) Bar() apicodec.Codec { // This is a problem, since apicodec is an internal package.
    // ...
}
```

While technically this problem is solved in Go 1.23 with explicit alias
types representation, (indeed, if you run mockgen on the example in the
linked issue with `GODEBUG=gotypesalias=1`, you get the expected
behavior) since we support the last two versions, we can't bump `go.mod`
to 1.23 yet. This leaves us with the old behavior, where `go/types` does
not track alias types. You can tell if an object is an alias, but not a
type itself, and there is no way to retrieve the object of interest at
the point where we are recursively parsing method types.

This PR works around this issue (temporarily) by using syntax
information to find all references to aliases in the source package.
When we find one, we record it in a mapping of underlying type -> alias
name. Later, while we parse the type tree, we replace any underlying
types in the mapping with their alias names.

The unexpected side effect of this is that _all_ references to the
underlying type in the generated mocks will be replaced with the alias,
even if the source used the underlying name. This is fine because:
* If the alias is in the mapping, it was used at least once, which means
its accessible.
* From a type-checking perspective, aliases and their underlying types
are equivalent.

The nice exception to the side effect is when we explicitly request mock
generation for an alias type, since at that point we are dealing with
the object, not the type.

With this PR, the mocks get generated correctly now:
```go
func (m *MockFoo) Bar() Codec {
    // ...
}
```

Once we can bump `go.mod` to 1.23, we should definitely remove this,
since the new type alias type nodes solve this problem automatically.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants