Repository: golang/mock
Pull request #640 (2022-05-03 20:26:08)

add support for generics for source mode
Updates: #621

codyoss: Needs a bunch of tests and things still, but should not take too much more to get this working fully
codyoss: Also, still need to look into reflect support.
omichels: i have tried to use this fork but found out that the generated mocks are simply missing the type information
codyoss: @omichels It is still a WIP progress ATM, but getting closer. I have not committed everything yet either. I will ping on the linked issue when I think the PR is in a good state and see if anyone is still having issues. Thanks for being an early tester though!
codyoss: @omichels Would you mind trying again to see if you are still seeing issues?
omichels: > @omichels Would you mind trying again to see if you are still seeing issues?

Looks fine now. Thank you so much.


This is our use case:

"our" API, which we want to mock:
```
type ResourceGroupClient interface {
  NewListPager(options *armresources.ResourceGroupsClientListOptions) *runtime.Pager[armresources.ResourceGroupsClientListResponse]
}
```

Azure sdk:
```
type Pager[T any] struct {
	current   *T
	processor PageProcessor[T]
	firstPage bool
}

type PageProcessor[T any] struct {
	More func(T) bool
	Fetcher func(context.Context, *T) (T, error)
}
```






steve-gray: Hey @codyoss  - Keen to give this a spin as we're being burned with some code changes - in essence we can't regenerate mocks on some services now as a common package we have uses generics internally. 

When I try to install with the CLI, it bombs out due to the go.mod paths not matching - there a way to force the install?

``` 
go install github.com/codyoss/mock/mockgen@891f4736814577b05df08811fd90798a66bdde38


go: github.com/codyoss/mock/mockgen@891f4736814577b05df08811fd90798a66bdde38: github.com/codyoss/mock@v1.6.1-0.20220506173251-891f47368145: parsing go.mod:
        module declares its path as: github.com/golang/mock
                but was required as: github.com/codyoss/mock
```
codyoss: @steve-gray I don' think you can go install from a fork, but I could be wrong. You should be able to check out this code locally though and run `go install ./...` to build from source. Let me know how that works for you

As a side not I do plan on trying to get at least this PR merged sometime this week. But there are still likely a couple of edge cases and reflect mode support before I officially cut a release.
steve-gray: Seems to quietly do its thing without erroring, tests are running again. Cheers @codyoss  - I'll keep an eye out for the full release 🚀 

Patch:
diff --git a/mockgen/generic_go118.go b/mockgen/generic_go118.go
new file mode 100644
index 0000000..b29db9a
--- /dev/null
+++ b/mockgen/generic_go118.go
@@ -0,0 +1,88 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build go1.18
+// +build go1.18
+
+package main
+
+import (
+	"go/ast"
+	"strings"
+
+	"github.com/golang/mock/mockgen/model"
+)
+
+func getTypeSpecTypeParams(ts *ast.TypeSpec) []*ast.Field {
+	if ts == nil || ts.TypeParams == nil {
+		return nil
+	}
+	return ts.TypeParams.List
+}
+
+func (p *fileParser) parseGenericType(pkg string, typ ast.Expr, tps map[string]bool) (model.Type, error) {
+	switch v := typ.(type) {
+	case *ast.IndexExpr:
+		m, err := p.parseType(pkg, v.X, tps)
+		if err != nil {
+			return nil, err
+		}
+		nm, ok := m.(*model.NamedType)
+		if !ok {
+			return m, nil
+		}
+		t, err := p.parseType(pkg, v.Index, tps)
+		if err != nil {
+			return nil, err
+		}
+		nm.TypeParams = &model.TypeParametersType{TypeParameters: []model.Type{t}}
+		return m, nil
+	case *ast.IndexListExpr:
+		m, err := p.parseType(pkg, v.X, tps)
+		if err != nil {
+			return nil, err
+		}
+		nm, ok := m.(*model.NamedType)
+		if !ok {
+			return m, nil
+		}
+		var ts []model.Type
+		for _, expr := range v.Indices {
+			t, err := p.parseType(pkg, expr, tps)
+			if err != nil {
+				return nil, err
+			}
+			ts = append(ts, t)
+		}
+		nm.TypeParams = &model.TypeParametersType{TypeParameters: ts}
+		return m, nil
+	}
+	return nil, nil
+}
+
+func getIdentTypeParams(decl interface{}) string {
+	if decl == nil {
+		return ""
+	}
+	ts, ok := decl.(*ast.TypeSpec)
+	if !ok {
+		return ""
+	}
+	if ts.TypeParams == nil || len(ts.TypeParams.List) == 0 {
+		return ""
+	}
+	var sb strings.Builder
+	sb.WriteString("[")
+	for i, v := range ts.TypeParams.List {
+		if i != 0 {
+			sb.WriteString(", ")
+		}
+		sb.WriteString(v.Names[0].Name)
+	}
+	sb.WriteString("]")
+	return sb.String()
+}
diff --git a/mockgen/generic_notgo118.go b/mockgen/generic_notgo118.go
new file mode 100644
index 0000000..8fe48c1
--- /dev/null
+++ b/mockgen/generic_notgo118.go
@@ -0,0 +1,36 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build !go1.18
+// +build !go1.18
+
+package main
+
+import (
+	"go/ast"
+
+	"github.com/golang/mock/mockgen/model"
+)
+
+func getTypeSpecTypeParams(ts *ast.TypeSpec) []*ast.Field {
+	return nil
+}
+
+func (p *fileParser) parseGenericType(pkg string, typ ast.Expr, tps map[string]bool) (model.Type, error) {
+	return nil, nil
+}
+
+func getIdentTypeParams(decl interface{}) string {
+	return ""
+}
diff --git a/mockgen/mockgen.go b/mockgen/mockgen.go
index 5048707..72a1c96 100644
--- a/mockgen/mockgen.go
+++ b/mockgen/mockgen.go
@@ -371,32 +371,58 @@ func (g *generator) mockName(typeName string) string {
 	return "Mock" + typeName
 }
 
+// formattedTypeParams returns a long and short form of type param info used for
+// printing. If analyzing a interface with type param [I any, O any] the result
+// will be:
+// "[I any, O any]", "[I, O]"
+func (g *generator) formattedTypeParams(it *model.Interface, pkgOverride string) (string, string) {
+	if len(it.TypeParams) == 0 {
+		return "", ""
+	}
+	var long, short strings.Builder
+	long.WriteString("[")
+	short.WriteString("[")
+	for i, v := range it.TypeParams {
+		if i != 0 {
+			long.WriteString(", ")
+			short.WriteString(", ")
+		}
+		long.WriteString(v.Name)
+		short.WriteString(v.Name)
+		long.WriteString(fmt.Sprintf(" %s", v.Type.String(g.packageMap, pkgOverride)))
+	}
+	long.WriteString("]")
+	short.WriteString("]")
+	return long.String(), short.String()
+}
+
 func (g *generator) GenerateMockInterface(intf *model.Interface, outputPackagePath string) error {
 	mockType := g.mockName(intf.Name)
+	longTp, shortTp := g.formattedTypeParams(intf, outputPackagePath)
 
 	g.p("")
 	g.p("// %v is a mock of %v interface.", mockType, intf.Name)
-	g.p("type %v struct {", mockType)
+	g.p("type %v%v struct {", mockType, longTp)
 	g.in()
 	g.p("ctrl     *gomock.Controller")
-	g.p("recorder *%vMockRecorder", mockType)
+	g.p("recorder *%vMockRecorder%v", mockType, shortTp)
 	g.out()
 	g.p("}")
 	g.p("")
 
 	g.p("// %vMockRecorder is the mock recorder for %v.", mockType, mockType)
-	g.p("type %vMockRecorder struct {", mockType)
+	g.p("type %vMockRecorder%v struct {", mockType, longTp)
 	g.in()
-	g.p("mock *%v", mockType)
+	g.p("mock *%v%v", mockType, shortTp)
 	g.out()
 	g.p("}")
 	g.p("")
 
 	g.p("// New%v creates a new mock instance.", mockType)
-	g.p("func New%v(ctrl *gomock.Controller) *%v {", mockType, mockType)
+	g.p("func New%v%v(ctrl *gomock.Controller) *%v%v {", mockType, longTp, mockType, shortTp)
 	g.in()
-	g.p("mock := &%v{ctrl: ctrl}", mockType)
-	g.p("mock.recorder = &%vMockRecorder{mock}", mockType)
+	g.p("mock := &%v%v{ctrl: ctrl}", mockType, shortTp)
+	g.p("mock.recorder = &%vMockRecorder%v{mock}", mockType, shortTp)
 	g.p("return mock")
 	g.out()
 	g.p("}")
@@ -404,13 +430,13 @@ func (g *generator) GenerateMockInterface(intf *model.Interface, outputPackagePa
 
 	// XXX: possible name collision here if someone has EXPECT in their interface.
 	g.p("// EXPECT returns an object that allows the caller to indicate expected use.")
-	g.p("func (m *%v) EXPECT() *%vMockRecorder {", mockType, mockType)
+	g.p("func (m *%v%v) EXPECT() *%vMockRecorder%v {", mockType, shortTp, mockType, shortTp)
 	g.in()
 	g.p("return m.recorder")
 	g.out()
 	g.p("}")
 
-	g.GenerateMockMethods(mockType, intf, outputPackagePath)
+	g.GenerateMockMethods(mockType, intf, outputPackagePath, shortTp)
 
 	return nil
 }
@@ -421,13 +447,13 @@ func (b byMethodName) Len() int           { return len(b) }
 func (b byMethodName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
 func (b byMethodName) Less(i, j int) bool { return b[i].Name < b[j].Name }
 
-func (g *generator) GenerateMockMethods(mockType string, intf *model.Interface, pkgOverride string) {
+func (g *generator) GenerateMockMethods(mockType string, intf *model.Interface, pkgOverride, shortTp string) {
 	sort.Sort(byMethodName(intf.Methods))
 	for _, m := range intf.Methods {
 		g.p("")
-		_ = g.GenerateMockMethod(mockType, m, pkgOverride)
+		_ = g.GenerateMockMethod(mockType, m, pkgOverride, shortTp)
 		g.p("")
-		_ = g.GenerateMockRecorderMethod(mockType, m)
+		_ = g.GenerateMockRecorderMethod(mockType, m, shortTp)
 	}
 }
 
@@ -446,7 +472,7 @@ func makeArgString(argNames, argTypes []string) string {
 
 // GenerateMockMethod generates a mock method implementation.
 // If non-empty, pkgOverride is the package in which unqualified types reside.
-func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOverride string) error {
+func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOverride, shortTp string) error {
 	argNames := g.getArgNames(m)
 	argTypes := g.getArgTypes(m, pkgOverride)
 	argString := makeArgString(argNames, argTypes)
@@ -467,7 +493,7 @@ func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOver
 	idRecv := ia.allocateIdentifier("m")
 
 	g.p("// %v mocks base method.", m.Name)
-	g.p("func (%v *%v) %v(%v)%v {", idRecv, mockType, m.Name, argString, retString)
+	g.p("func (%v *%v%v) %v(%v)%v {", idRecv, mockType, shortTp, m.Name, argString, retString)
 	g.in()
 	g.p("%s.ctrl.T.Helper()", idRecv)
 
@@ -511,7 +537,7 @@ func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOver
 	return nil
 }
 
-func (g *generator) GenerateMockRecorderMethod(mockType string, m *model.Method) error {
+func (g *generator) GenerateMockRecorderMethod(mockType string, m *model.Method, shortTp string) error {
 	argNames := g.getArgNames(m)
 
 	var argString string
@@ -535,7 +561,7 @@ func (g *generator) GenerateMockRecorderMethod(mockType string, m *model.Method)
 	idRecv := ia.allocateIdentifier("mr")
 
 	g.p("// %v indicates an expected call of %v.", m.Name, m.Name)
-	g.p("func (%s *%vMockRecorder) %v(%v) *gomock.Call {", idRecv, mockType, m.Name, argString)
+	g.p("func (%s *%vMockRecorder%v) %v(%v) *gomock.Call {", idRecv, mockType, shortTp, m.Name, argString)
 	g.in()
 	g.p("%s.mock.ctrl.T.Helper()", idRecv)
 
@@ -558,7 +584,7 @@ func (g *generator) GenerateMockRecorderMethod(mockType string, m *model.Method)
 			callArgs = ", " + idVarArgs + "..."
 		}
 	}
-	g.p(`return %s.mock.ctrl.RecordCallWithMethodType(%s.mock, "%s", reflect.TypeOf((*%s)(nil).%s)%s)`, idRecv, idRecv, m.Name, mockType, m.Name, callArgs)
+	g.p(`return %s.mock.ctrl.RecordCallWithMethodType(%s.mock, "%s", reflect.TypeOf((*%s%s)(nil).%s)%s)`, idRecv, idRecv, m.Name, mockType, shortTp, m.Name, callArgs)
 
 	g.out()
 	g.p("}")
diff --git a/mockgen/model/model.go b/mockgen/model/model.go
index 2c6a62c..94d7f4b 100644
--- a/mockgen/model/model.go
+++ b/mockgen/model/model.go
@@ -47,14 +47,18 @@ func (pkg *Package) Imports() map[string]bool {
 	im := make(map[string]bool)
 	for _, intf := range pkg.Interfaces {
 		intf.addImports(im)
+		for _, tp := range intf.TypeParams {
+			tp.Type.addImports(im)
+		}
 	}
 	return im
 }
 
 // Interface is a Go interface.
 type Interface struct {
-	Name    string
-	Methods []*Method
+	Name       string
+	Methods    []*Method
+	TypeParams []*Parameter
 }
 
 // Print writes the interface name and its methods.
@@ -259,26 +263,28 @@ func (mt *MapType) addImports(im map[string]bool) {
 
 // NamedType is an exported type in a package.
 type NamedType struct {
-	Package string // may be empty
-	Type    string
+	Package    string // may be empty
+	Type       string
+	TypeParams *TypeParametersType
 }
 
 func (nt *NamedType) String(pm map[string]string, pkgOverride string) string {
 	if pkgOverride == nt.Package {
-		return nt.Type
+		return nt.Type + nt.TypeParams.String(pm, pkgOverride)
 	}
 	prefix := pm[nt.Package]
 	if prefix != "" {
-		return prefix + "." + nt.Type
+		return prefix + "." + nt.Type + nt.TypeParams.String(pm, pkgOverride)
 	}
 
-	return nt.Type
+	return nt.Type + nt.TypeParams.String(pm, pkgOverride)
 }
 
 func (nt *NamedType) addImports(im map[string]bool) {
 	if nt.Package != "" {
 		im[nt.Package] = true
 	}
+	nt.TypeParams.addImports(im)
 }
 
 // PointerType is a pointer to another type.
@@ -297,6 +303,36 @@ type PredeclaredType string
 func (pt PredeclaredType) String(map[string]string, string) string { return string(pt) }
 func (pt PredeclaredType) addImports(map[string]bool)              {}
 
+// TypeParametersType contains type paramters for a NamedType.
+type TypeParametersType struct {
+	TypeParameters []Type
+}
+
+func (tp *TypeParametersType) String(pm map[string]string, pkgOverride string) string {
+	if tp == nil || len(tp.TypeParameters) == 0 {
+		return ""
+	}
+	var sb strings.Builder
+	sb.WriteString("[")
+	for i, v := range tp.TypeParameters {
+		if i != 0 {
+			sb.WriteString(", ")
+		}
+		sb.WriteString(v.String(pm, pkgOverride))
+	}
+	sb.WriteString("]")
+	return sb.String()
+}
+
+func (tp *TypeParametersType) addImports(im map[string]bool) {
+	if tp == nil {
+		return
+	}
+	for _, v := range tp.TypeParameters {
+		v.addImports(im)
+	}
+}
+
 // The following code is intended to be called by the program generated by ../reflect.go.
 
 // InterfaceFromInterfaceType returns a pointer to an interface for the
diff --git a/mockgen/parse.go b/mockgen/parse.go
index b7be203..21c0d70 100644
--- a/mockgen/parse.go
+++ b/mockgen/parse.go
@@ -62,8 +62,8 @@ func sourceMode(source string) (*model.Package, error) {
 	p := &fileParser{
 		fileSet:            fs,
 		imports:            make(map[string]importedPackage),
-		importedInterfaces: make(map[string]map[string]*ast.InterfaceType),
-		auxInterfaces:      make(map[string]map[string]*ast.InterfaceType),
+		importedInterfaces: newInterfaceCache(),
+		auxInterfaces:      newInterfaceCache(),
 		srcDir:             srcDir,
 	}
 
@@ -127,15 +127,48 @@ func (d duplicateImport) Error() string {
 func (d duplicateImport) Path() string        { log.Fatal(d.Error()); return "" }
 func (d duplicateImport) Parser() *fileParser { log.Fatal(d.Error()); return nil }
 
-type fileParser struct {
-	fileSet            *token.FileSet
-	imports            map[string]importedPackage               // package name => imported package
-	importedInterfaces map[string]map[string]*ast.InterfaceType // package (or "") => name => interface
+type interfaceCache struct {
+	m map[string]map[string]*namedInterface
+}
 
-	auxFiles      []*ast.File
-	auxInterfaces map[string]map[string]*ast.InterfaceType // package (or "") => name => interface
+func newInterfaceCache() *interfaceCache {
+	return &interfaceCache{
+		m: make(map[string]map[string]*namedInterface),
+	}
+}
 
-	srcDir string
+func (i *interfaceCache) Set(pkg, name string, it *namedInterface) {
+	if _, ok := i.m[pkg]; !ok {
+		i.m[pkg] = make(map[string]*namedInterface)
+	}
+	i.m[pkg][name] = it
+}
+
+func (i *interfaceCache) Get(pkg, name string) *namedInterface {
+	if _, ok := i.m[pkg]; !ok {
+		return nil
+	}
+	return i.m[pkg][name]
+}
+
+func (i *interfaceCache) GetASTIface(pkg, name string) *ast.InterfaceType {
+	if _, ok := i.m[pkg]; !ok {
+		return nil
+	}
+	it, ok := i.m[pkg][name]
+	if !ok {
+		return nil
+	}
+	return it.it
+}
+
+type fileParser struct {
+	fileSet            *token.FileSet
+	imports            map[string]importedPackage // package name => imported package
+	importedInterfaces *interfaceCache
+	auxFiles           []*ast.File
+	auxInterfaces      *interfaceCache
+	srcDir             string
 }
 
 func (p *fileParser) errorf(pos token.Pos, format string, args ...interface{}) error {
@@ -168,11 +201,8 @@ func (p *fileParser) parseAuxFiles(auxFiles string) error {
 }
 
 func (p *fileParser) addAuxInterfacesFromFile(pkg string, file *ast.File) {
-	if _, ok := p.auxInterfaces[pkg]; !ok {
-		p.auxInterfaces[pkg] = make(map[string]*ast.InterfaceType)
-	}
 	for ni := range iterInterfaces(file) {
-		p.auxInterfaces[pkg][ni.name.Name] = ni.it
+		p.auxInterfaces.Set(pkg, ni.name.Name, ni)
 	}
 }
 
@@ -199,7 +229,7 @@ func (p *fileParser) parseFile(importPath string, file *ast.File) (*model.Packag
 
 	var is []*model.Interface
 	for ni := range iterInterfaces(file) {
-		i, err := p.parseInterface(ni.name.String(), importPath, ni.it)
+		i, err := p.parseInterface(ni.name.String(), importPath, ni)
 		if err != nil {
 			return nil, err
 		}
@@ -219,8 +249,8 @@ func (p *fileParser) parsePackage(path string) (*fileParser, error) {
 	newP := &fileParser{
 		fileSet:            token.NewFileSet(),
 		imports:            make(map[string]importedPackage),
-		importedInterfaces: make(map[string]map[string]*ast.InterfaceType),
-		auxInterfaces:      make(map[string]map[string]*ast.InterfaceType),
+		importedInterfaces: newInterfaceCache(),
+		auxInterfaces:      newInterfaceCache(),
 		srcDir:             p.srcDir,
 	}
 
@@ -233,11 +263,8 @@ func (p *fileParser) parsePackage(path string) (*fileParser, error) {
 
 	for _, pkg := range pkgs {
 		file := ast.MergePackageFiles(pkg, ast.FilterFuncDuplicates|ast.FilterUnassociatedComments|ast.FilterImportDuplicates)
-		if _, ok := newP.importedInterfaces[path]; !ok {
-			newP.importedInterfaces[path] = make(map[string]*ast.InterfaceType)
-		}
 		for ni := range iterInterfaces(file) {
-			newP.importedInterfaces[path][ni.name.Name] = ni.it
+			newP.importedInterfaces.Set(path, ni.name.Name, ni)
 		}
 		imports, _ := importsOfFile(file)
 		for pkgName, pkgI := range imports {
@@ -247,9 +274,20 @@ func (p *fileParser) parsePackage(path string) (*fileParser, error) {
 	return newP, nil
 }
 
-func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*model.Interface, error) {
+func (p *fileParser) parseInterface(name, pkg string, it *namedInterface) (*model.Interface, error) {
 	iface := &model.Interface{Name: name}
-	for _, field := range it.Methods.List {
+	tps := make(map[string]bool)
+
+	tp, err := p.parseFieldList(pkg, it.typeParams, tps)
+	if err != nil {
+		return nil, fmt.Errorf("unable to parse interface type parameters: %v", name)
+	}
+	iface.TypeParams = tp
+	for _, v := range tp {
+		tps[v.Name] = true
+	}
+
+	for _, field := range it.it.Methods.List {
 		switch v := field.Type.(type) {
 		case *ast.FuncType:
 			if nn := len(field.Names); nn != 1 {
@@ -259,16 +297,16 @@ func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*m
 				Name: field.Names[0].String(),
 			}
 			var err error
-			m.In, m.Variadic, m.Out, err = p.parseFunc(pkg, v)
+			m.In, m.Variadic, m.Out, err = p.parseFunc(pkg, v, tps)
 			if err != nil {
 				return nil, err
 			}
 			iface.AddMethod(m)
 		case *ast.Ident:
 			// Embedded interface in this package.
-			embeddedIfaceType := p.auxInterfaces[pkg][v.String()]
+			embeddedIfaceType := p.auxInterfaces.Get(pkg, v.String())
 			if embeddedIfaceType == nil {
-				embeddedIfaceType = p.importedInterfaces[pkg][v.String()]
+				embeddedIfaceType = p.importedInterfaces.Get(pkg, v.String())
 			}
 
 			var embeddedIface *model.Interface
@@ -288,7 +326,7 @@ func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*m
 						return nil, p.errorf(v.Pos(), "could not parse package %s: %v", pkg, err)
 					}
 
-					if embeddedIfaceType = ip.importedInterfaces[pkg][v.String()]; embeddedIfaceType == nil {
+					if embeddedIfaceType = ip.importedInterfaces.Get(pkg, v.String()); embeddedIfaceType == nil {
 						return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", pkg, v.String())
 					}
 
@@ -312,7 +350,7 @@ func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*m
 
 			var embeddedIface *model.Interface
 			var err error
-			embeddedIfaceType := p.auxInterfaces[filePkg][sel]
+			embeddedIfaceType := p.auxInterfaces.Get(filePkg, sel)
 			if embeddedIfaceType != nil {
 				embeddedIface, err = p.parseInterface(sel, filePkg, embeddedIfaceType)
 				if err != nil {
@@ -332,7 +370,7 @@ func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*m
 						parser: parser,
 					}
 				}
-				if embeddedIfaceType = parser.importedInterfaces[path][sel]; embeddedIfaceType == nil {
+				if embeddedIfaceType = parser.importedInterfaces.Get(path, sel); embeddedIfaceType == nil {
 					return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", path, sel)
 				}
 				embeddedIface, err = parser.parseInterface(sel, path, embeddedIfaceType)
@@ -352,26 +390,26 @@ func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*m
 	return iface, nil
 }
 
-func (p *fileParser) parseFunc(pkg string, f *ast.FuncType) (inParam []*model.Parameter, variadic *model.Parameter, outParam []*model.Parameter, err error) {
+func (p *fileParser) parseFunc(pkg string, f *ast.FuncType, tps map[string]bool) (inParam []*model.Parameter, variadic *model.Parameter, outParam []*model.Parameter, err error) {
 	if f.Params != nil {
 		regParams := f.Params.List
 		if isVariadic(f) {
 			n := len(regParams)
 			varParams := regParams[n-1:]
 			regParams = regParams[:n-1]
-			vp, err := p.parseFieldList(pkg, varParams)
+			vp, err := p.parseFieldList(pkg, varParams, tps)
 			if err != nil {
 				return nil, nil, nil, p.errorf(varParams[0].Pos(), "failed parsing variadic argument: %v", err)
 			}
 			variadic = vp[0]
 		}
-		inParam, err = p.parseFieldList(pkg, regParams)
+		inParam, err = p.parseFieldList(pkg, regParams, tps)
 		if err != nil {
 			return nil, nil, nil, p.errorf(f.Pos(), "failed parsing arguments: %v", err)
 		}
 	}
 	if f.Results != nil {
-		outParam, err = p.parseFieldList(pkg, f.Results.List)
+		outParam, err = p.parseFieldList(pkg, f.Results.List, tps)
 		if err != nil {
 			return nil, nil, nil, p.errorf(f.Pos(), "failed parsing returns: %v", err)
 		}
@@ -379,7 +417,7 @@ func (p *fileParser) parseFunc(pkg string, f *ast.FuncType) (inParam []*model.Pa
 	return
 }
 
-func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field) ([]*model.Parameter, error) {
+func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field, tps map[string]bool) ([]*model.Parameter, error) {
 	nf := 0
 	for _, f := range fields {
 		nn := len(f.Names)
@@ -394,7 +432,7 @@ func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field) ([]*model.P
 	ps := make([]*model.Parameter, nf)
 	i := 0 // destination index
 	for _, f := range fields {
-		t, err := p.parseType(pkg, f.Type)
+		t, err := p.parseType(pkg, f.Type, tps)
 		if err != nil {
 			return nil, err
 		}
@@ -413,7 +451,7 @@ func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field) ([]*model.P
 	return ps, nil
 }
 
-func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) {
+func (p *fileParser) parseType(pkg string, typ ast.Expr, tps map[string]bool) (model.Type, error) {
 	switch v := typ.(type) {
 	case *ast.ArrayType:
 		ln := -1
@@ -427,13 +465,13 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) {
 				return nil, p.errorf(v.Len.Pos(), "bad array size: %v", err)
 			}
 		}
-		t, err := p.parseType(pkg, v.Elt)
+		t, err := p.parseType(pkg, v.Elt, tps)
 		if err != nil {
 			return nil, err
 		}
 		return &model.ArrayType{Len: ln, Type: t}, nil
 	case *ast.ChanType:
-		t, err := p.parseType(pkg, v.Value)
+		t, err := p.parseType(pkg, v.Value, tps)
 		if err != nil {
 			return nil, err
 		}
@@ -447,15 +485,15 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) {
 		return &model.ChanType{Dir: dir, Type: t}, nil
 	case *ast.Ellipsis:
 		// assume we're parsing a variadic argument
-		return p.parseType(pkg, v.Elt)
+		return p.parseType(pkg, v.Elt, tps)
 	case *ast.FuncType:
-		in, variadic, out, err := p.parseFunc(pkg, v)
+		in, variadic, out, err := p.parseFunc(pkg, v, tps)
 		if err != nil {
 			return nil, err
 		}
 		return &model.FuncType{In: in, Out: out, Variadic: variadic}, nil
 	case *ast.Ident:
-		if v.IsExported() {
+		if v.IsExported() && !tps[v.Name] {
 			// `pkg` may be an aliased imported pkg
 			// if so, patch the import w/ the fully qualified import
 			maybeImportedPkg, ok := p.imports[pkg]
@@ -474,11 +512,11 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) {
 		}
 		return model.PredeclaredType("interface{}"), nil
 	case *ast.MapType:
-		key, err := p.parseType(pkg, v.Key)
+		key, err := p.parseType(pkg, v.Key, tps)
 		if err != nil {
 			return nil, err
 		}
-		value, err := p.parseType(pkg, v.Value)
+		value, err := p.parseType(pkg, v.Value, tps)
 		if err != nil {
 			return nil, err
 		}
@@ -491,7 +529,7 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) {
 		}
 		return &model.NamedType{Package: pkg.Path(), Type: v.Sel.String()}, nil
 	case *ast.StarExpr:
-		t, err := p.parseType(pkg, v.X)
+		t, err := p.parseType(pkg, v.X, tps)
 		if err != nil {
 			return nil, err
 		}
@@ -502,7 +540,16 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) {
 		}
 		return model.PredeclaredType("struct{}"), nil
 	case *ast.ParenExpr:
-		return p.parseType(pkg, v.X)
+		return p.parseType(pkg, v.X, tps)
+	default:
+		mt, err := p.parseGenericType(pkg, typ, tps)
+		if err != nil {
+			return nil, err
+		}
+		if mt == nil {
+			break
+		}
+		return mt, nil
 	}
 
 	return nil, fmt.Errorf("don't know how to parse type %T", typ)
@@ -610,13 +657,14 @@ func importsOfFile(file *ast.File) (normalImports map[string]importedPackage, do
 }
 
 type namedInterface struct {
-	name *ast.Ident
-	it   *ast.InterfaceType
+	name       *ast.Ident
+	it         *ast.InterfaceType
+	typeParams []*ast.Field
 }
 
 // Create an iterator over all interfaces in file.
-func iterInterfaces(file *ast.File) <-chan namedInterface {
-	ch := make(chan namedInterface)
+func iterInterfaces(file *ast.File) <-chan *namedInterface {
+	ch := make(chan *namedInterface)
 	go func() {
 		for _, decl := range file.Decls {
 			gd, ok := decl.(*ast.GenDecl)
@@ -633,7 +681,7 @@ func iterInterfaces(file *ast.File) <-chan namedInterface {
 					continue
 				}
 
-				ch <- namedInterface{ts.Name, it}
+				ch <- &namedInterface{ts.Name, it, getTypeSpecTypeParams(ts)}
 			}
 		}
 		close(ch)
diff --git a/mockgen/version.go b/mockgen/version.go
index 3be1f46..6db160a 100644
--- a/mockgen/version.go
+++ b/mockgen/version.go
@@ -1,4 +1,4 @@
-// Copyright 2019 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -28,5 +28,4 @@ func printModuleVersion() {
 			"GO111MODULE=on when running 'go get' in order to use specific " +
 			"version of the binary.")
 	}
-
 }


Test patch:
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index e0a83e7..b5e2c25 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -37,10 +37,16 @@ jobs:
         ./ci/test.sh
         ./ci/check_panic_handling.sh
 
-    - name: Run Go tests
+    - name: Run Go tests all
+      if: ${{ startsWith(matrix.go-version, '1.18') }}
       run: |
         for i in $(find $PWD -name go.mod); do
           pushd $(dirname $i)
           go test ./...
           popd
         done 
+
+    - name: Run Go tests some
+      if: ${{ startsWith(matrix.go-version, '1.18') == false }}
+      run: |
+          go test ./...
diff --git a/mockgen/internal/tests/generics/external.go b/mockgen/internal/tests/generics/external.go
new file mode 100644
index 0000000..ec5032a
--- /dev/null
+++ b/mockgen/internal/tests/generics/external.go
@@ -0,0 +1,21 @@
+package generics
+
+import (
+	"github.com/golang/mock/mockgen/internal/tests/generics/other"
+	"golang.org/x/exp/constraints"
+)
+
+//go:generate mockgen --source=external.go --destination=source/mock_external_test.go --package source
+
+type ExternalConstraint[I constraints.Integer, F constraints.Float] interface {
+	One(string) string
+	Two(I) string
+	Three(I) F
+	Four(I) Foo[I, F]
+	Five(I) Baz[F]
+	Six(I) *Baz[F]
+	Seven(I) other.One[I]
+	Eight(F) other.Two[I, F]
+	Nine(Iface[I])
+	Ten(*I)
+}
diff --git a/mockgen/internal/tests/generics/generics.go b/mockgen/internal/tests/generics/generics.go
new file mode 100644
index 0000000..0b38962
--- /dev/null
+++ b/mockgen/internal/tests/generics/generics.go
@@ -0,0 +1,40 @@
+package generics
+
+import "github.com/golang/mock/mockgen/internal/tests/generics/other"
+
+//go:generate mockgen --source=generics.go --destination=source/mock_generics_test.go --package source
+////go:generate mockgen --destination=reflect/mock_test.go --package reflect . Bar,Bar2
+
+type Bar[T any, R any] interface {
+	One(string) string
+	Two(T) string
+	Three(T) R
+	Four(T) Foo[T, R]
+	Five(T) Baz[T]
+	Six(T) *Baz[T]
+	Seven(T) other.One[T]
+	Eight(T) other.Two[T, R]
+	Nine(Iface[T])
+	Ten(*T)
+	Eleven() (*other.One[T], error)
+	Twelve() (*other.Two[T, R], error)
+	Thirteen() (Baz[StructType], error)
+	Fourteen() (*Foo[StructType, StructType2], error)
+	Fifteen() (Iface[StructType], error)
+	Sixteen() (Baz[other.Three], error)
+	Seventeen() (*Foo[other.Three, other.Four], error)
+	Eighteen() (Iface[*other.Five], error)
+	Nineteen() AliasType
+}
+
+type Foo[T any, R any] struct{}
+
+type Baz[T any] struct{}
+
+type Iface[T any] interface{}
+
+type StructType struct{}
+
+type StructType2 struct{}
+
+type AliasType Baz[other.Three]
diff --git a/mockgen/internal/tests/generics/go.mod b/mockgen/internal/tests/generics/go.mod
new file mode 100644
index 0000000..7eb82e1
--- /dev/null
+++ b/mockgen/internal/tests/generics/go.mod
@@ -0,0 +1,10 @@
+module github.com/golang/mock/mockgen/internal/tests/generics
+
+go 1.18
+
+require (
+	github.com/golang/mock v1.6.0
+	golang.org/x/exp v0.0.0-20220428152302-39d4317da171
+)
+
+replace github.com/golang/mock => ../../../..
diff --git a/mockgen/internal/tests/generics/go.sum b/mockgen/internal/tests/generics/go.sum
new file mode 100644
index 0000000..c6f4464
--- /dev/null
+++ b/mockgen/internal/tests/generics/go.sum
@@ -0,0 +1,26 @@
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20220428152302-39d4317da171 h1:TfdoLivD44QwvssI9Sv1xwa5DcL5XQr4au4sZ2F2NV4=
+golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
+golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/mockgen/internal/tests/generics/other/other.go b/mockgen/internal/tests/generics/other/other.go
new file mode 100644
index 0000000..9265422
--- /dev/null
+++ b/mockgen/internal/tests/generics/other/other.go
@@ -0,0 +1,11 @@
+package other
+
+type One[T any] struct{}
+
+type Two[T any, R any] struct{}
+
+type Three struct{}
+
+type Four struct{}
+
+type Five interface{}
diff --git a/mockgen/internal/tests/generics/source/mock_external_test.go b/mockgen/internal/tests/generics/source/mock_external_test.go
new file mode 100644
index 0000000..aab2274
--- /dev/null
+++ b/mockgen/internal/tests/generics/source/mock_external_test.go
@@ -0,0 +1,173 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: external.go
+
+// Package source is a generated GoMock package.
+package source
+
+import (
+	reflect "reflect"
+
+	gomock "github.com/golang/mock/gomock"
+	generics "github.com/golang/mock/mockgen/internal/tests/generics"
+	other "github.com/golang/mock/mockgen/internal/tests/generics/other"
+	constraints "golang.org/x/exp/constraints"
+)
+
+// MockExternalConstraint is a mock of ExternalConstraint interface.
+type MockExternalConstraint[I constraints.Integer, F constraints.Float] struct {
+	ctrl     *gomock.Controller
+	recorder *MockExternalConstraintMockRecorder[I, F]
+}
+
+// MockExternalConstraintMockRecorder is the mock recorder for MockExternalConstraint.
+type MockExternalConstraintMockRecorder[I constraints.Integer, F constraints.Float] struct {
+	mock *MockExternalConstraint[I, F]
+}
+
+// NewMockExternalConstraint creates a new mock instance.
+func NewMockExternalConstraint[I constraints.Integer, F constraints.Float](ctrl *gomock.Controller) *MockExternalConstraint[I, F] {
+	mock := &MockExternalConstraint[I, F]{ctrl: ctrl}
+	mock.recorder = &MockExternalConstraintMockRecorder[I, F]{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockExternalConstraint[I, F]) EXPECT() *MockExternalConstraintMockRecorder[I, F] {
+	return m.recorder
+}
+
+// Eight mocks base method.
+func (m *MockExternalConstraint[I, F]) Eight(arg0 F) other.Two[I, F] {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Eight", arg0)
+	ret0, _ := ret[0].(other.Two[I, F])
+	return ret0
+}
+
+// Eight indicates an expected call of Eight.
+func (mr *MockExternalConstraintMockRecorder[I, F]) Eight(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Eight", reflect.TypeOf((*MockExternalConstraint[I, F])(nil).Eight), arg0)
+}
+
+// Five mocks base method.
+func (m *MockExternalConstraint[I, F]) Five(arg0 I) generics.Baz[F] {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Five", arg0)
+	ret0, _ := ret[0].(generics.Baz[F])
+	return ret0
+}
+
+// Five indicates an expected call of Five.
+func (mr *MockExternalConstraintMockRecorder[I, F]) Five(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Five", reflect.TypeOf((*MockExternalConstraint[I, F])(nil).Five), arg0)
+}
+
+// Four mocks base method.
+func (m *MockExternalConstraint[I, F]) Four(arg0 I) generics.Foo[I, F] {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Four", arg0)
+	ret0, _ := ret[0].(generics.Foo[I, F])
+	return ret0
+}
+
+// Four indicates an expected call of Four.
+func (mr *MockExternalConstraintMockRecorder[I, F]) Four(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Four", reflect.TypeOf((*MockExternalConstraint[I, F])(nil).Four), arg0)
+}
+
+// Nine mocks base method.
+func (m *MockExternalConstraint[I, F]) Nine(arg0 generics.Iface[I]) {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "Nine", arg0)
+}
+
+// Nine indicates an expected call of Nine.
+func (mr *MockExternalConstraintMockRecorder[I, F]) Nine(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Nine", reflect.TypeOf((*MockExternalConstraint[I, F])(nil).Nine), arg0)
+}
+
+// One mocks base method.
+func (m *MockExternalConstraint[I, F]) One(arg0 string) string {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "One", arg0)
+	ret0, _ := ret[0].(string)
+	return ret0
+}
+
+// One indicates an expected call of One.
+func (mr *MockExternalConstraintMockRecorder[I, F]) One(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "One", reflect.TypeOf((*MockExternalConstraint[I, F])(nil).One), arg0)
+}
+
+// Seven mocks base method.
+func (m *MockExternalConstraint[I, F]) Seven(arg0 I) other.One[I] {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Seven", arg0)
+	ret0, _ := ret[0].(other.One[I])
+	return ret0
+}
+
+// Seven indicates an expected call of Seven.
+func (mr *MockExternalConstraintMockRecorder[I, F]) Seven(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Seven", reflect.TypeOf((*MockExternalConstraint[I, F])(nil).Seven), arg0)
+}
+
+// Six mocks base method.
+func (m *MockExternalConstraint[I, F]) Six(arg0 I) *generics.Baz[F] {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Six", arg0)
+	ret0, _ := ret[0].(*generics.Baz[F])
+	return ret0
+}
+
+// Six indicates an expected call of Six.
+func (mr *MockExternalConstraintMockRecorder[I, F]) Six(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Six", reflect.TypeOf((*MockExternalConstraint[I, F])(nil).Six), arg0)
+}
+
+// Ten mocks base method.
+func (m *MockExternalConstraint[I, F]) Ten(arg0 *I) {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "Ten", arg0)
+}
+
+// Ten indicates an expected call of Ten.
+func (mr *MockExternalConstraintMockRecorder[I, F]) Ten(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ten", reflect.TypeOf((*MockExternalConstraint[I, F])(nil).Ten), arg0)
+}
+
+// Three mocks base method.
+func (m *MockExternalConstraint[I, F]) Three(arg0 I) F {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Three", arg0)
+	ret0, _ := ret[0].(F)
+	return ret0
+}
+
+// Three indicates an expected call of Three.
+func (mr *MockExternalConstraintMockRecorder[I, F]) Three(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Three", reflect.TypeOf((*MockExternalConstraint[I, F])(nil).Three), arg0)
+}
+
+// Two mocks base method.
+func (m *MockExternalConstraint[I, F]) Two(arg0 I) string {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Two", arg0)
+	ret0, _ := ret[0].(string)
+	return ret0
+}
+
+// Two indicates an expected call of Two.
+func (mr *MockExternalConstraintMockRecorder[I, F]) Two(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Two", reflect.TypeOf((*MockExternalConstraint[I, F])(nil).Two), arg0)
+}
diff --git a/mockgen/internal/tests/generics/source/mock_generics_test.go b/mockgen/internal/tests/generics/source/mock_generics_test.go
new file mode 100644
index 0000000..0223e31
--- /dev/null
+++ b/mockgen/internal/tests/generics/source/mock_generics_test.go
@@ -0,0 +1,329 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: generics.go
+
+// Package source is a generated GoMock package.
+package source
+
+import (
+	reflect "reflect"
+
+	gomock "github.com/golang/mock/gomock"
+	generics "github.com/golang/mock/mockgen/internal/tests/generics"
+	other "github.com/golang/mock/mockgen/internal/tests/generics/other"
+)
+
+// MockBar is a mock of Bar interface.
+type MockBar[T any, R any] struct {
+	ctrl     *gomock.Controller
+	recorder *MockBarMockRecorder[T, R]
+}
+
+// MockBarMockRecorder is the mock recorder for MockBar.
+type MockBarMockRecorder[T any, R any] struct {
+	mock *MockBar[T, R]
+}
+
+// NewMockBar creates a new mock instance.
+func NewMockBar[T any, R any](ctrl *gomock.Controller) *MockBar[T, R] {
+	mock := &MockBar[T, R]{ctrl: ctrl}
+	mock.recorder = &MockBarMockRecorder[T, R]{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockBar[T, R]) EXPECT() *MockBarMockRecorder[T, R] {
+	return m.recorder
+}
+
+// Eight mocks base method.
+func (m *MockBar[T, R]) Eight(arg0 T) other.Two[T, R] {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Eight", arg0)
+	ret0, _ := ret[0].(other.Two[T, R])
+	return ret0
+}
+
+// Eight indicates an expected call of Eight.
+func (mr *MockBarMockRecorder[T, R]) Eight(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Eight", reflect.TypeOf((*MockBar[T, R])(nil).Eight), arg0)
+}
+
+// Eighteen mocks base method.
+func (m *MockBar[T, R]) Eighteen() (generics.Iface[*other.Five], error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Eighteen")
+	ret0, _ := ret[0].(generics.Iface[*other.Five])
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Eighteen indicates an expected call of Eighteen.
+func (mr *MockBarMockRecorder[T, R]) Eighteen() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Eighteen", reflect.TypeOf((*MockBar[T, R])(nil).Eighteen))
+}
+
+// Eleven mocks base method.
+func (m *MockBar[T, R]) Eleven() (*other.One[T], error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Eleven")
+	ret0, _ := ret[0].(*other.One[T])
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Eleven indicates an expected call of Eleven.
+func (mr *MockBarMockRecorder[T, R]) Eleven() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Eleven", reflect.TypeOf((*MockBar[T, R])(nil).Eleven))
+}
+
+// Fifteen mocks base method.
+func (m *MockBar[T, R]) Fifteen() (generics.Iface[generics.StructType], error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Fifteen")
+	ret0, _ := ret[0].(generics.Iface[generics.StructType])
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Fifteen indicates an expected call of Fifteen.
+func (mr *MockBarMockRecorder[T, R]) Fifteen() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fifteen", reflect.TypeOf((*MockBar[T, R])(nil).Fifteen))
+}
+
+// Five mocks base method.
+func (m *MockBar[T, R]) Five(arg0 T) generics.Baz[T] {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Five", arg0)
+	ret0, _ := ret[0].(generics.Baz[T])
+	return ret0
+}
+
+// Five indicates an expected call of Five.
+func (mr *MockBarMockRecorder[T, R]) Five(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Five", reflect.TypeOf((*MockBar[T, R])(nil).Five), arg0)
+}
+
+// Four mocks base method.
+func (m *MockBar[T, R]) Four(arg0 T) generics.Foo[T, R] {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Four", arg0)
+	ret0, _ := ret[0].(generics.Foo[T, R])
+	return ret0
+}
+
+// Four indicates an expected call of Four.
+func (mr *MockBarMockRecorder[T, R]) Four(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Four", reflect.TypeOf((*MockBar[T, R])(nil).Four), arg0)
+}
+
+// Fourteen mocks base method.
+func (m *MockBar[T, R]) Fourteen() (*generics.Foo[generics.StructType, generics.StructType2], error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Fourteen")
+	ret0, _ := ret[0].(*generics.Foo[generics.StructType, generics.StructType2])
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Fourteen indicates an expected call of Fourteen.
+func (mr *MockBarMockRecorder[T, R]) Fourteen() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fourteen", reflect.TypeOf((*MockBar[T, R])(nil).Fourteen))
+}
+
+// Nine mocks base method.
+func (m *MockBar[T, R]) Nine(arg0 generics.Iface[T]) {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "Nine", arg0)
+}
+
+// Nine indicates an expected call of Nine.
+func (mr *MockBarMockRecorder[T, R]) Nine(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Nine", reflect.TypeOf((*MockBar[T, R])(nil).Nine), arg0)
+}
+
+// Nineteen mocks base method.
+func (m *MockBar[T, R]) Nineteen() generics.AliasType {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Nineteen")
+	ret0, _ := ret[0].(generics.AliasType)
+	return ret0
+}
+
+// Nineteen indicates an expected call of Nineteen.
+func (mr *MockBarMockRecorder[T, R]) Nineteen() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Nineteen", reflect.TypeOf((*MockBar[T, R])(nil).Nineteen))
+}
+
+// One mocks base method.
+func (m *MockBar[T, R]) One(arg0 string) string {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "One", arg0)
+	ret0, _ := ret[0].(string)
+	return ret0
+}
+
+// One indicates an expected call of One.
+func (mr *MockBarMockRecorder[T, R]) One(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "One", reflect.TypeOf((*MockBar[T, R])(nil).One), arg0)
+}
+
+// Seven mocks base method.
+func (m *MockBar[T, R]) Seven(arg0 T) other.One[T] {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Seven", arg0)
+	ret0, _ := ret[0].(other.One[T])
+	return ret0
+}
+
+// Seven indicates an expected call of Seven.
+func (mr *MockBarMockRecorder[T, R]) Seven(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Seven", reflect.TypeOf((*MockBar[T, R])(nil).Seven), arg0)
+}
+
+// Seventeen mocks base method.
+func (m *MockBar[T, R]) Seventeen() (*generics.Foo[other.Three, other.Four], error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Seventeen")
+	ret0, _ := ret[0].(*generics.Foo[other.Three, other.Four])
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Seventeen indicates an expected call of Seventeen.
+func (mr *MockBarMockRecorder[T, R]) Seventeen() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Seventeen", reflect.TypeOf((*MockBar[T, R])(nil).Seventeen))
+}
+
+// Six mocks base method.
+func (m *MockBar[T, R]) Six(arg0 T) *generics.Baz[T] {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Six", arg0)
+	ret0, _ := ret[0].(*generics.Baz[T])
+	return ret0
+}
+
+// Six indicates an expected call of Six.
+func (mr *MockBarMockRecorder[T, R]) Six(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Six", reflect.TypeOf((*MockBar[T, R])(nil).Six), arg0)
+}
+
+// Sixteen mocks base method.
+func (m *MockBar[T, R]) Sixteen() (generics.Baz[other.Three], error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Sixteen")
+	ret0, _ := ret[0].(generics.Baz[other.Three])
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Sixteen indicates an expected call of Sixteen.
+func (mr *MockBarMockRecorder[T, R]) Sixteen() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sixteen", reflect.TypeOf((*MockBar[T, R])(nil).Sixteen))
+}
+
+// Ten mocks base method.
+func (m *MockBar[T, R]) Ten(arg0 *T) {
+	m.ctrl.T.Helper()
+	m.ctrl.Call(m, "Ten", arg0)
+}
+
+// Ten indicates an expected call of Ten.
+func (mr *MockBarMockRecorder[T, R]) Ten(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ten", reflect.TypeOf((*MockBar[T, R])(nil).Ten), arg0)
+}
+
+// Thirteen mocks base method.
+func (m *MockBar[T, R]) Thirteen() (generics.Baz[generics.StructType], error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Thirteen")
+	ret0, _ := ret[0].(generics.Baz[generics.StructType])
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Thirteen indicates an expected call of Thirteen.
+func (mr *MockBarMockRecorder[T, R]) Thirteen() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Thirteen", reflect.TypeOf((*MockBar[T, R])(nil).Thirteen))
+}
+
+// Three mocks base method.
+func (m *MockBar[T, R]) Three(arg0 T) R {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Three", arg0)
+	ret0, _ := ret[0].(R)
+	return ret0
+}
+
+// Three indicates an expected call of Three.
+func (mr *MockBarMockRecorder[T, R]) Three(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Three", reflect.TypeOf((*MockBar[T, R])(nil).Three), arg0)
+}
+
+// Twelve mocks base method.
+func (m *MockBar[T, R]) Twelve() (*other.Two[T, R], error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Twelve")
+	ret0, _ := ret[0].(*other.Two[T, R])
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Twelve indicates an expected call of Twelve.
+func (mr *MockBarMockRecorder[T, R]) Twelve() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Twelve", reflect.TypeOf((*MockBar[T, R])(nil).Twelve))
+}
+
+// Two mocks base method.
+func (m *MockBar[T, R]) Two(arg0 T) string {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Two", arg0)
+	ret0, _ := ret[0].(string)
+	return ret0
+}
+
+// Two indicates an expected call of Two.
+func (mr *MockBarMockRecorder[T, R]) Two(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	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
+}
diff --git a/mockgen/parse_test.go b/mockgen/parse_test.go
index 2a290cb..82c831f 100644
--- a/mockgen/parse_test.go
+++ b/mockgen/parse_test.go
@@ -1,7 +1,6 @@
 package main
 
 import (
-	"go/ast"
 	"go/parser"
 	"go/token"
 	"testing"
@@ -17,7 +16,7 @@ func TestFileParser_ParseFile(t *testing.T) {
 	p := fileParser{
 		fileSet:            fs,
 		imports:            make(map[string]importedPackage),
-		importedInterfaces: make(map[string]map[string]*ast.InterfaceType),
+		importedInterfaces: newInterfaceCache(),
 	}
 
 	pkg, err := p.parseFile("", file)
@@ -48,7 +47,7 @@ func TestFileParser_ParsePackage(t *testing.T) {
 	p := fileParser{
 		fileSet:            fs,
 		imports:            make(map[string]importedPackage),
-		importedInterfaces: make(map[string]map[string]*ast.InterfaceType),
+		importedInterfaces: newInterfaceCache(),
 	}
 
 	newP, err := p.parsePackage("github.com/golang/mock/mockgen/internal/tests/custom_package_name/greeter")
@@ -126,8 +125,8 @@ func TestParseArrayWithConstLength(t *testing.T) {
 	p := fileParser{
 		fileSet:            fs,
 		imports:            make(map[string]importedPackage),
-		importedInterfaces: make(map[string]map[string]*ast.InterfaceType),
-		auxInterfaces:      make(map[string]map[string]*ast.InterfaceType),
+		importedInterfaces: newInterfaceCache(),
+		auxInterfaces:      newInterfaceCache(),
 		srcDir:             srcDir,
 	}