Skip to content

Commit e36794b

Browse files
committed
Package Mode: Use aliases when used in source
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.
1 parent b8222fa commit e36794b

File tree

7 files changed

+802
-9
lines changed

7 files changed

+802
-9
lines changed
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package alias
2+
3+
//go:generate mockgen -typed -package=mock -destination=mock/interfaces.go . Fooer,FooerAlias,Barer,BarerAlias,Bazer,QuxerConsumer,QuuxerConsumer
4+
5+
import "go.uber.org/mock/mockgen/internal/tests/alias/subpkg"
6+
7+
// Case 1: A interface that has alias references in this package
8+
// should still be generated for its underlying name, i.e., MockFooer,
9+
// even though we have the alias replacement logic.
10+
type Fooer interface {
11+
Foo()
12+
}
13+
14+
// Case 2: Generating a mock for an alias type.
15+
type FooerAlias = Fooer
16+
17+
// Case 3: Generate mock for an interface that takes in alias parameters
18+
// and returns alias results.
19+
type Barer interface{
20+
Bar(FooerAlias) FooerAlias
21+
}
22+
23+
// Case 4: Combination of cases 2 & 3.
24+
type BarerAlias = Barer
25+
26+
// Case 5: Generate mock for an interface that actually returns
27+
// the underlying type. This will generate mocks that use the alias,
28+
// but that should be fine since they should be interchangeable.
29+
type Bazer interface{
30+
Baz(Fooer) Fooer
31+
}
32+
33+
// Case 6: Generate mock for a type that refers to an alias defined in this package
34+
// for a type from another package.
35+
// The generated methods should use the alias defined here.
36+
type QuxerAlias = subpkg.Quxer
37+
38+
type QuxerConsumer interface{
39+
Consume(QuxerAlias) QuxerAlias
40+
}
41+
42+
// Case 7: Generate mock for a type that refers to an alias defined in another package
43+
// for an unexported type in that other package.
44+
// The generated method should only use the alias, not the unexported underlying name.
45+
type QuuxerConsumer interface{
46+
Consume(subpkg.Quuxer) subpkg.Quuxer
47+
}

0 commit comments

Comments
 (0)