Skip to content

Commit

Permalink
Handle interface method name conflicts in cmg (#527)
Browse files Browse the repository at this point in the history
- Fix #260 by checking if the `Set<method>`, `Set<method>`, and
  `HasMore` methods are already part of an interface and if they are,
  adding a `Mock` suffix, and if that is still not unique adding a
  unique numeric suffix after that. Examples: `Set<method>Mock`,
  `Add<method>Mock`, `HasMoreMock1`.
- Use `runtime/debug.BuildInfo` to find the version in `cmg` since
  golang/go#50603 is implemented with Go 1.24 which Clue now requires.
  • Loading branch information
douglaswth authored Feb 26, 2025
1 parent 6fbc395 commit 456b083
Show file tree
Hide file tree
Showing 12 changed files with 302 additions and 72 deletions.
117 changes: 117 additions & 0 deletions go.work.sum

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions mock/cmd/cmg/pkg/generate/_tests/conflicts/conflicts.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ package testing
type (
Conflicts interface {
Simple(c *Conflicts) *Conflicts
AddSimple()
SetSimple()
HasMore()
HasMoreMock()
}
)
80 changes: 76 additions & 4 deletions mock/cmd/cmg/pkg/generate/_tests/conflicts/mocks/conflicts.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions mock/cmd/cmg/pkg/generate/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type (

importMap map[string]Import

import_ struct {
importImpl struct {
pkgPath, pkgName, alias string
}
)
Expand All @@ -36,22 +36,22 @@ func newImport(pkgPath string, args ...string) Import {
pkgName = args[0]
alias = args[1]
}
return &import_{pkgPath: pkgPath, pkgName: pkgName, alias: alias}
return &importImpl{pkgPath: pkgPath, pkgName: pkgName, alias: alias}
}

func (i *import_) PkgName() string {
func (i *importImpl) PkgName() string {
return i.pkgName
}

func (i *import_) PkgPath() string {
func (i *importImpl) PkgPath() string {
return i.pkgPath
}

func (i *import_) Alias() string {
func (i *importImpl) Alias() string {
return i.alias
}

func (i *import_) AliasOrPkgName() string {
func (i *importImpl) AliasOrPkgName() string {
if i.alias != "" {
return i.alias
}
Expand Down
69 changes: 58 additions & 11 deletions mock/cmd/cmg/pkg/generate/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package generate

import (
"fmt"
"math"
"strconv"
"strings"

"goa.design/clue/mock/cmd/cmg/pkg/parse"
Expand All @@ -17,24 +19,33 @@ type (
Methods() []Method
MaxFuncLenFmt() string
Var() string
HasMore() string
}

interface_ struct {
interfaceImpl struct {
parse.Interface

methods []Method
maxFuncLen int
typeNames, typeZeros typeMap
hasMoreName string
}

interfaceScope map[string]struct{}
)

func newInterface(i parse.Interface, typeNames, typeZeros typeMap, stdImports, extImports, intImports importMap, modPath string) Interface {
for _, p := range i.TypeParameters() {
addType(p.Constraint(), typeNames, typeZeros, stdImports, extImports, intImports, modPath)
}
iface := &interface_{i, nil, 0, typeNames, typeZeros}
for _, m := range i.Methods() {
method := newMethod(m, i, typeNames, typeZeros, stdImports, extImports, intImports, modPath)
var (
iface = &interfaceImpl{Interface: i, typeNames: typeNames, typeZeros: typeZeros}
ms = i.Methods()
is = newInterfaceScope(ms)
)
iface.hasMoreName = is.uniqueName("HasMore")
for _, m := range ms {
method := newMethod(m, i, typeNames, typeZeros, stdImports, extImports, intImports, modPath, is)
if l := len(method.Func() + iface.TypeParameters()); l > iface.maxFuncLen {
iface.maxFuncLen = l
}
Expand All @@ -43,15 +54,15 @@ func newInterface(i parse.Interface, typeNames, typeZeros typeMap, stdImports, e
return iface
}

func (i *interface_) Constructor() string {
func (i *interfaceImpl) Constructor() string {
return "New" + i.Name()
}

func (i *interface_) ConstructorFmt(pkgName string) string {
func (i *interfaceImpl) ConstructorFmt(pkgName string) string {
return fmt.Sprintf("%%-%vv", 3+len(pkgName)+len(i.Name())+len(i.TypeParameterVars()))
}

func (i *interface_) TypeParameters() string {
func (i *interfaceImpl) TypeParameters() string {
ps := i.Interface.TypeParameters()
if len(ps) == 0 {
return ""
Expand All @@ -67,7 +78,7 @@ func (i *interface_) TypeParameters() string {
return "[" + strings.Join(parameters, ", ") + "]"
}

func (i *interface_) TypeParameterVars() string {
func (i *interfaceImpl) TypeParameterVars() string {
ps := i.Interface.TypeParameters()
if len(ps) == 0 {
return ""
Expand All @@ -79,14 +90,50 @@ func (i *interface_) TypeParameterVars() string {
return "[" + strings.Join(vars, ", ") + "]"
}

func (i *interface_) Methods() []Method {
func (i *interfaceImpl) Methods() []Method {
return i.methods
}

func (i *interface_) Var() string {
func (i *interfaceImpl) Var() string {
return "m"
}

func (i *interface_) MaxFuncLenFmt() string {
func (i *interfaceImpl) MaxFuncLenFmt() string {
return fmt.Sprintf("%%-%vv", i.maxFuncLen)
}

func (i *interfaceImpl) HasMore() string {
return i.hasMoreName
}

func newInterfaceScope(ms []parse.Method) interfaceScope {
is := make(interfaceScope, len(ms))
for _, m := range ms {
is[m.Name()] = struct{}{}
}
return is
}

func (is interfaceScope) uniqueName(name string) string {
_, ok := is[name]
if !ok {
is[name] = struct{}{}
return name
}

name += "Mock"
if _, ok := is[name]; !ok {
is[name] = struct{}{}
return name
}

var newName string
for i := 1; i <= math.MaxInt; i++ {
newName = name + strconv.Itoa(i)
if _, ok := is[newName]; !ok {
is[newName] = struct{}{}
break
}
}
return newName
}
28 changes: 14 additions & 14 deletions mock/cmd/cmg/pkg/generate/method.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ type (
method struct {
parse.Method

func_, add, set, interfaceVar, funcVar string
typeNames, typeZeros typeMap
funcName, addName, setName, interfaceVar, funcVar string
typeNames, typeZeros typeMap
}
)

func newMethod(m parse.Method, i parse.Interface, typeNames, typeZeros typeMap, stdImports, extImports, intImports importMap, modPath string) Method {
func newMethod(m parse.Method, i parse.Interface, typeNames, typeZeros typeMap, stdImports, extImports, intImports importMap, modPath string, is interfaceScope) Method {
parameterVars := make(map[string]struct{})
for _, t := range m.Parameters() {
parameterVars[t.Name()] = struct{}{}
Expand All @@ -40,27 +40,27 @@ func newMethod(m parse.Method, i parse.Interface, typeNames, typeZeros typeMap,
addType(t.Type(), typeNames, typeZeros, stdImports, extImports, intImports, modPath)
}
return &method{
m,
i.Name() + m.Name() + "Func",
"Add" + m.Name(),
"Set" + m.Name(),
uniqueVar("m", parameterVars),
uniqueVar("f", parameterVars),
typeNames,
typeZeros,
Method: m,
funcName: i.Name() + m.Name() + "Func",
addName: is.uniqueName("Add" + m.Name()),
setName: is.uniqueName("Set" + m.Name()),
interfaceVar: uniqueVar("m", parameterVars),
funcVar: uniqueVar("f", parameterVars),
typeNames: typeNames,
typeZeros: typeZeros,
}
}

func (m *method) Func() string {
return m.func_
return m.funcName
}

func (m *method) Add() string {
return m.add
return m.addName
}

func (m *method) Set() string {
return m.set
return m.setName
}

func (m *method) InterfaceVar() string {
Expand Down
2 changes: 1 addition & 1 deletion mock/cmd/cmg/pkg/generate/mocks.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func ({{ .InterfaceVar }} *{{ $interface.Name }}{{ $interface.TypeParameterVars
}
{{- end }}

func ({{ $interface.Var }} *{{ $interface.Name }}{{ $interface.TypeParameterVars }}) HasMore() bool {
func ({{ $interface.Var }} *{{ $interface.Name }}{{ $interface.TypeParameterVars }}) {{ .HasMore }}() bool {
return {{ $interface.Var }}.m.HasMore()
}
{{- end }}
16 changes: 8 additions & 8 deletions mock/cmd/cmg/pkg/parse/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type (
Methods() []Method
}

interface_ struct {
interfaceImpl struct {
p *packages.Package
file string
typeSpec *ast.TypeSpec
Expand All @@ -25,22 +25,22 @@ type (
)

func newInterface(p *packages.Package, file string, typeSpec *ast.TypeSpec, interfaceType *ast.InterfaceType) Interface {
return &interface_{p: p, file: file, typeSpec: typeSpec, interfaceType: interfaceType}
return &interfaceImpl{p: p, file: file, typeSpec: typeSpec, interfaceType: interfaceType}
}

func (i *interface_) Name() string {
func (i *interfaceImpl) Name() string {
return i.typeSpec.Name.Name
}

func (i *interface_) IsExported() bool {
func (i *interfaceImpl) IsExported() bool {
return i.typeSpec.Name.IsExported()
}

func (i *interface_) File() string {
func (i *interfaceImpl) File() string {
return i.file
}

func (i *interface_) TypeParameters() (typeParameters []Type) {
func (i *interfaceImpl) TypeParameters() (typeParameters []Type) {
if i.typeSpec.TypeParams != nil {
for _, tp := range i.typeSpec.TypeParams.List {
for _, ident := range tp.Names {
Expand All @@ -51,11 +51,11 @@ func (i *interface_) TypeParameters() (typeParameters []Type) {
return
}

func (i *interface_) Methods() []Method {
func (i *interfaceImpl) Methods() []Method {
return i.methods(i.interfaceType)
}

func (i *interface_) methods(it *ast.InterfaceType) (methods []Method) {
func (i *interfaceImpl) methods(it *ast.InterfaceType) (methods []Method) {
for _, m := range it.Methods.List {
switch t := m.Type.(type) {
case *ast.FuncType:
Expand Down
Loading

0 comments on commit 456b083

Please sign in to comment.