From ce0236ec032f8c37ca2f46dcf8fc523bea61d64a Mon Sep 17 00:00:00 2001 From: LandonTClipp <11232769+LandonTClipp@users.noreply.github.com> Date: Wed, 1 Jan 2025 15:35:45 -0600 Subject: [PATCH] Fix issues with in-package detection and name conflict resolution --- cmd/mockery.go | 31 ++---- pkg/config.go | 62 ------------ pkg/errors.go | 11 +++ .../iface_new_type/iface_new_type_test.go | 8 -- .../mock_interfaceA_test.go | 69 +++++++------- .../mock_interfaceB0_test.go | 27 +++--- .../type_alias/mock_Interface1_test.go | 13 ++- .../type_alias/mock_Interface2_test.go | 9 +- pkg/registry/method_scope.go | 16 ++-- pkg/registry/registry.go | 11 ++- pkg/template_generator.go | 95 ++++++++++++++++++- 11 files changed, 187 insertions(+), 165 deletions(-) create mode 100644 pkg/errors.go diff --git a/cmd/mockery.go b/cmd/mockery.go index cf392e1d..bb9ea1b3 100644 --- a/cmd/mockery.go +++ b/cmd/mockery.go @@ -171,7 +171,6 @@ func GetRootAppFromViper(v *viper.Viper) (*RootApp, error) { // uniform. type InterfaceCollection struct { srcPkgPath string - outPkgPath string outFilePath *pathlib.Path srcPkg *packages.Package interfaces []*pkg.Interface @@ -179,13 +178,11 @@ type InterfaceCollection struct { func NewInterfaceCollection( srcPkgPath string, - outPkgPath string, outFilePath *pathlib.Path, srcPkg *packages.Package, ) *InterfaceCollection { return &InterfaceCollection{ srcPkgPath: srcPkgPath, - outPkgPath: outPkgPath, outFilePath: outFilePath, srcPkg: srcPkg, interfaces: make([]*pkg.Interface, 0), @@ -208,15 +205,6 @@ func (i *InterfaceCollection) Append(ctx context.Context, iface *pkg.Interface) log.Error().Msg(msg) return errors.New(msg) } - ifacePkgPath, err := iface.Config.PkgPath() - if err != nil { - return err - } - if ifacePkgPath != i.outPkgPath { - msg := "all mocks within an InterfaceCollection must have the same output package path" - log.Error().Msg(msg) - return errors.New(msg) - } i.interfaces = append(i.interfaces, iface) return nil } @@ -293,16 +281,11 @@ func (r *RootApp) Run() error { return err } filePath := ifaceConfig.FilePath(ctx).String() - outPkgPath, err := ifaceConfig.PkgPath() - if err != nil { - return err - } _, ok := mockFileToInterfaces[filePath] if !ok { mockFileToInterfaces[filePath] = NewInterfaceCollection( iface.Pkg.PkgPath, - outPkgPath, pathlib.NewPath(ifaceConfig.Dir).Join(ifaceConfig.FileName), iface.Pkg, ) @@ -324,8 +307,6 @@ func (r *RootApp) Run() error { fileCtx := fileLog.WithContext(ctx) fileLog.Debug().Int("interfaces-in-file-len", len(interfacesInFile.interfaces)).Msgf("%v", interfacesInFile) - outPkgPath := interfacesInFile.outPkgPath - packageConfig, err := r.Config.GetPackageConfig(fileCtx, interfacesInFile.srcPkgPath) if err != nil { return err @@ -334,8 +315,9 @@ func (r *RootApp) Run() error { return err } generator, err := pkg.NewTemplateGenerator( + fileCtx, interfacesInFile.interfaces[0].Pkg, - outPkgPath, + interfacesInFile.outFilePath.Parent(), packageConfig.Template, pkg.Formatter(r.Config.Formatter), packageConfig, @@ -347,8 +329,13 @@ func (r *RootApp) Run() error { if err != nil { return err } - if err := pathlib.NewPath(outFilePath).WriteFile(templateBytes); err != nil { - return err + outFile := pathlib.NewPath(outFilePath) + if err := outFile.Parent().MkdirAll(); err != nil { + log.Err(err).Msg("failed to mkdir parent directories of mock file") + return stackerr.NewStackErr(err) + } + if err := outFile.WriteFile(templateBytes); err != nil { + return stackerr.NewStackErr(err) } } diff --git a/pkg/config.go b/pkg/config.go index 8214f311..00dce5c8 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -26,14 +26,6 @@ import ( "gopkg.in/yaml.v3" ) -var ( - ErrNoConfigFile = fmt.Errorf("no config file exists") - ErrNoGoFilesFoundInRoot = fmt.Errorf("no go files found in root search path") - ErrPkgNotFound = fmt.Errorf("package not found in config") - ErrGoModNotFound = fmt.Errorf("no go.mod file found") - ErrGoModInvalid = fmt.Errorf("go.mod file has no module line") -) - type Config struct { All bool `mapstructure:"all"` Anchors map[string]any `mapstructure:"_anchors"` @@ -927,60 +919,6 @@ func (c *Config) ParseTemplates(ctx context.Context, iface *Interface, srcPkg *p return nil } -// PkgPath returns the fully qualified package path of the output mock that this config -// represents. For example, it might return "github.com/vektra/mockery/v3/internal". -// This function will error if it cannot find a base go.mod file. -func (c *Config) PkgPath() (string, error) { - dirPath := pathlib.NewPath(c.Dir) - if err := dirPath.MkdirAll(); err != nil { - return "", stackerr.NewStackErr(err) - } - dir, err := pathlib.NewPath(c.Dir).ResolveAll() - if err != nil { - return "", stackerr.NewStackErr(err) - } - var goModFile *pathlib.Path - for i := 0; ; i++ { - if i == 1000 { - return "", stackerr.NewStackErr(errors.New("failed to find go.mod after 1000 iterations")) - } - goMod := dir.Join("go.mod") - goModExists, err := goMod.Exists() - if err != nil { - return "", stackerr.NewStackErr(err) - } - if !goModExists { - parent := dir.Parent() - // Hit the root path - if dir.String() == parent.String() { - return "", stackerr.NewStackErrf( - ErrGoModNotFound, "parsing package path for %s", c.Dir) - } - dir = parent - continue - } - goModFile = goMod - break - } - fileBytes, err := goModFile.ReadFile() - if err != nil { - return "", stackerr.NewStackErr(err) - } - scanner := bufio.NewScanner(bytes.NewReader(fileBytes)) - // Iterate over each line - for scanner.Scan() { - if !strings.HasPrefix(scanner.Text(), "module") { - continue - } - moduleName := strings.Split(scanner.Text(), "module ")[1] - return pathlib.NewPath( - moduleName, - pathlib.PathWithSeperator("/")). - Join(c.Dir).String(), nil - } - return "", stackerr.NewStackErr(ErrGoModInvalid) -} - func (c *Config) ClearCfgAsMap() { c._cfgAsMap = nil } diff --git a/pkg/errors.go b/pkg/errors.go new file mode 100644 index 00000000..dd360d42 --- /dev/null +++ b/pkg/errors.go @@ -0,0 +1,11 @@ +package pkg + +import "fmt" + +var ( + ErrNoConfigFile = fmt.Errorf("no config file exists") + ErrNoGoFilesFoundInRoot = fmt.Errorf("no go files found in root search path") + ErrPkgNotFound = fmt.Errorf("package not found in config") + ErrGoModNotFound = fmt.Errorf("no go.mod file found") + ErrGoModInvalid = fmt.Errorf("go.mod file has no module line") +) diff --git a/pkg/fixtures/iface_new_type/iface_new_type_test.go b/pkg/fixtures/iface_new_type/iface_new_type_test.go index 90ac5f7f..8bb915d7 100644 --- a/pkg/fixtures/iface_new_type/iface_new_type_test.go +++ b/pkg/fixtures/iface_new_type/iface_new_type_test.go @@ -8,12 +8,4 @@ func TestUsage(t *testing.T) { interface1 := NewMockInterface1(t) interface1.EXPECT().Method1().Return() interface1.Method1() - - interface2 := NewMockInterface2(t) - interface2.EXPECT().Method1().Return() - interface2.Method1() - - interface3 := NewMockInterface3(t) - interface3.EXPECT().Method1().Return() - interface3.Method1() } diff --git a/pkg/fixtures/method_args/same_name_arg_and_type/mock_interfaceA_test.go b/pkg/fixtures/method_args/same_name_arg_and_type/mock_interfaceA_test.go index 8ec178b5..92f8dcf8 100644 --- a/pkg/fixtures/method_args/same_name_arg_and_type/mock_interfaceA_test.go +++ b/pkg/fixtures/method_args/same_name_arg_and_type/mock_interfaceA_test.go @@ -5,7 +5,6 @@ package same_name_arg_and_type import ( - "github.com/vektra/mockery/v2/pkg/fixtures/method_args/same_name_arg_and_type" mock "github.com/stretchr/testify/mock" ) @@ -41,20 +40,20 @@ func (_m *interfaceAMock) EXPECT() *interfaceAMock_Expecter { // DoB provides a mock function for the type interfaceAMock -func (_mock *interfaceAMock) DoB(interfaceB same_name_arg_and_type.interfaceB) same_name_arg_and_type.interfaceB { - ret := _mock.Called(interfaceB) +func (_mock *interfaceAMock) DoB(interfaceB1 interfaceB) interfaceB { + ret := _mock.Called(interfaceB1) if len(ret) == 0 { panic("no return value specified for DoB") } - var r0 same_name_arg_and_type.interfaceB - if returnFunc, ok := ret.Get(0).(func(same_name_arg_and_type.interfaceB) same_name_arg_and_type.interfaceB); ok { - r0 = returnFunc(interfaceB) + var r0 interfaceB + if returnFunc, ok := ret.Get(0).(func(interfaceB) interfaceB); ok { + r0 = returnFunc(interfaceB1) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(same_name_arg_and_type.interfaceB) + r0 = ret.Get(0).(interfaceB) } } return r0 @@ -70,31 +69,31 @@ type interfaceAMock_DoB_Call struct { // DoB is a helper method to define mock.On call -// - interfaceB -func (_e *interfaceAMock_Expecter) DoB(interfaceB interface{}, ) *interfaceAMock_DoB_Call { - return &interfaceAMock_DoB_Call{Call: _e.mock.On("DoB",interfaceB, )} +// - interfaceB1 +func (_e *interfaceAMock_Expecter) DoB(interfaceB1 interface{}, ) *interfaceAMock_DoB_Call { + return &interfaceAMock_DoB_Call{Call: _e.mock.On("DoB",interfaceB1, )} } -func (_c *interfaceAMock_DoB_Call) Run(run func(interfaceB same_name_arg_and_type.interfaceB)) *interfaceAMock_DoB_Call { +func (_c *interfaceAMock_DoB_Call) Run(run func(interfaceB1 interfaceB)) *interfaceAMock_DoB_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(same_name_arg_and_type.interfaceB),) + run(args[0].(interfaceB),) }) return _c } -func (_c *interfaceAMock_DoB_Call) Return(interfaceBMoqParam same_name_arg_and_type.interfaceB) *interfaceAMock_DoB_Call { +func (_c *interfaceAMock_DoB_Call) Return(interfaceBMoqParam interfaceB) *interfaceAMock_DoB_Call { _c.Call.Return(interfaceBMoqParam) return _c } -func (_c *interfaceAMock_DoB_Call) RunAndReturn(run func(interfaceB same_name_arg_and_type.interfaceB)same_name_arg_and_type.interfaceB) *interfaceAMock_DoB_Call { +func (_c *interfaceAMock_DoB_Call) RunAndReturn(run func(interfaceB1 interfaceB)interfaceB) *interfaceAMock_DoB_Call { _c.Call.Return(run) return _c } // DoB0 provides a mock function for the type interfaceAMock -func (_mock *interfaceAMock) DoB0(interfaceB same_name_arg_and_type.interfaceB0) same_name_arg_and_type.interfaceB0 { +func (_mock *interfaceAMock) DoB0(interfaceB interfaceB0) interfaceB0 { ret := _mock.Called(interfaceB) if len(ret) == 0 { @@ -102,12 +101,12 @@ func (_mock *interfaceAMock) DoB0(interfaceB same_name_arg_and_type.interfaceB0) } - var r0 same_name_arg_and_type.interfaceB0 - if returnFunc, ok := ret.Get(0).(func(same_name_arg_and_type.interfaceB0) same_name_arg_and_type.interfaceB0); ok { + var r0 interfaceB0 + if returnFunc, ok := ret.Get(0).(func(interfaceB0) interfaceB0); ok { r0 = returnFunc(interfaceB) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(same_name_arg_and_type.interfaceB0) + r0 = ret.Get(0).(interfaceB0) } } return r0 @@ -128,39 +127,39 @@ func (_e *interfaceAMock_Expecter) DoB0(interfaceB interface{}, ) *interfaceAMoc return &interfaceAMock_DoB0_Call{Call: _e.mock.On("DoB0",interfaceB, )} } -func (_c *interfaceAMock_DoB0_Call) Run(run func(interfaceB same_name_arg_and_type.interfaceB0)) *interfaceAMock_DoB0_Call { +func (_c *interfaceAMock_DoB0_Call) Run(run func(interfaceB interfaceB0)) *interfaceAMock_DoB0_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(same_name_arg_and_type.interfaceB0),) + run(args[0].(interfaceB0),) }) return _c } -func (_c *interfaceAMock_DoB0_Call) Return(interfaceB0MoqParam same_name_arg_and_type.interfaceB0) *interfaceAMock_DoB0_Call { +func (_c *interfaceAMock_DoB0_Call) Return(interfaceB0MoqParam interfaceB0) *interfaceAMock_DoB0_Call { _c.Call.Return(interfaceB0MoqParam) return _c } -func (_c *interfaceAMock_DoB0_Call) RunAndReturn(run func(interfaceB same_name_arg_and_type.interfaceB0)same_name_arg_and_type.interfaceB0) *interfaceAMock_DoB0_Call { +func (_c *interfaceAMock_DoB0_Call) RunAndReturn(run func(interfaceB interfaceB0)interfaceB0) *interfaceAMock_DoB0_Call { _c.Call.Return(run) return _c } // DoB0v2 provides a mock function for the type interfaceAMock -func (_mock *interfaceAMock) DoB0v2(interfaceB0 same_name_arg_and_type.interfaceB0) same_name_arg_and_type.interfaceB0 { - ret := _mock.Called(interfaceB0) +func (_mock *interfaceAMock) DoB0v2(interfaceB01 interfaceB0) interfaceB0 { + ret := _mock.Called(interfaceB01) if len(ret) == 0 { panic("no return value specified for DoB0v2") } - var r0 same_name_arg_and_type.interfaceB0 - if returnFunc, ok := ret.Get(0).(func(same_name_arg_and_type.interfaceB0) same_name_arg_and_type.interfaceB0); ok { - r0 = returnFunc(interfaceB0) + var r0 interfaceB0 + if returnFunc, ok := ret.Get(0).(func(interfaceB0) interfaceB0); ok { + r0 = returnFunc(interfaceB01) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(same_name_arg_and_type.interfaceB0) + r0 = ret.Get(0).(interfaceB0) } } return r0 @@ -176,24 +175,24 @@ type interfaceAMock_DoB0v2_Call struct { // DoB0v2 is a helper method to define mock.On call -// - interfaceB0 -func (_e *interfaceAMock_Expecter) DoB0v2(interfaceB0 interface{}, ) *interfaceAMock_DoB0v2_Call { - return &interfaceAMock_DoB0v2_Call{Call: _e.mock.On("DoB0v2",interfaceB0, )} +// - interfaceB01 +func (_e *interfaceAMock_Expecter) DoB0v2(interfaceB01 interface{}, ) *interfaceAMock_DoB0v2_Call { + return &interfaceAMock_DoB0v2_Call{Call: _e.mock.On("DoB0v2",interfaceB01, )} } -func (_c *interfaceAMock_DoB0v2_Call) Run(run func(interfaceB0 same_name_arg_and_type.interfaceB0)) *interfaceAMock_DoB0v2_Call { +func (_c *interfaceAMock_DoB0v2_Call) Run(run func(interfaceB01 interfaceB0)) *interfaceAMock_DoB0v2_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(same_name_arg_and_type.interfaceB0),) + run(args[0].(interfaceB0),) }) return _c } -func (_c *interfaceAMock_DoB0v2_Call) Return(interfaceB0MoqParam same_name_arg_and_type.interfaceB0) *interfaceAMock_DoB0v2_Call { +func (_c *interfaceAMock_DoB0v2_Call) Return(interfaceB0MoqParam interfaceB0) *interfaceAMock_DoB0v2_Call { _c.Call.Return(interfaceB0MoqParam) return _c } -func (_c *interfaceAMock_DoB0v2_Call) RunAndReturn(run func(interfaceB0 same_name_arg_and_type.interfaceB0)same_name_arg_and_type.interfaceB0) *interfaceAMock_DoB0v2_Call { +func (_c *interfaceAMock_DoB0v2_Call) RunAndReturn(run func(interfaceB01 interfaceB0)interfaceB0) *interfaceAMock_DoB0v2_Call { _c.Call.Return(run) return _c } diff --git a/pkg/fixtures/method_args/same_name_arg_and_type/mock_interfaceB0_test.go b/pkg/fixtures/method_args/same_name_arg_and_type/mock_interfaceB0_test.go index fd39133d..2abb96b5 100644 --- a/pkg/fixtures/method_args/same_name_arg_and_type/mock_interfaceB0_test.go +++ b/pkg/fixtures/method_args/same_name_arg_and_type/mock_interfaceB0_test.go @@ -5,7 +5,6 @@ package same_name_arg_and_type import ( - "github.com/vektra/mockery/v2/pkg/fixtures/method_args/same_name_arg_and_type" mock "github.com/stretchr/testify/mock" ) @@ -41,20 +40,20 @@ func (_m *interfaceB0Mock) EXPECT() *interfaceB0Mock_Expecter { // DoB0 provides a mock function for the type interfaceB0Mock -func (_mock *interfaceB0Mock) DoB0(interfaceB0 same_name_arg_and_type.interfaceB0) same_name_arg_and_type.interfaceB0 { - ret := _mock.Called(interfaceB0) +func (_mock *interfaceB0Mock) DoB0(interfaceB01 interfaceB0) interfaceB0 { + ret := _mock.Called(interfaceB01) if len(ret) == 0 { panic("no return value specified for DoB0") } - var r0 same_name_arg_and_type.interfaceB0 - if returnFunc, ok := ret.Get(0).(func(same_name_arg_and_type.interfaceB0) same_name_arg_and_type.interfaceB0); ok { - r0 = returnFunc(interfaceB0) + var r0 interfaceB0 + if returnFunc, ok := ret.Get(0).(func(interfaceB0) interfaceB0); ok { + r0 = returnFunc(interfaceB01) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(same_name_arg_and_type.interfaceB0) + r0 = ret.Get(0).(interfaceB0) } } return r0 @@ -70,24 +69,24 @@ type interfaceB0Mock_DoB0_Call struct { // DoB0 is a helper method to define mock.On call -// - interfaceB0 -func (_e *interfaceB0Mock_Expecter) DoB0(interfaceB0 interface{}, ) *interfaceB0Mock_DoB0_Call { - return &interfaceB0Mock_DoB0_Call{Call: _e.mock.On("DoB0",interfaceB0, )} +// - interfaceB01 +func (_e *interfaceB0Mock_Expecter) DoB0(interfaceB01 interface{}, ) *interfaceB0Mock_DoB0_Call { + return &interfaceB0Mock_DoB0_Call{Call: _e.mock.On("DoB0",interfaceB01, )} } -func (_c *interfaceB0Mock_DoB0_Call) Run(run func(interfaceB0 same_name_arg_and_type.interfaceB0)) *interfaceB0Mock_DoB0_Call { +func (_c *interfaceB0Mock_DoB0_Call) Run(run func(interfaceB01 interfaceB0)) *interfaceB0Mock_DoB0_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(same_name_arg_and_type.interfaceB0),) + run(args[0].(interfaceB0),) }) return _c } -func (_c *interfaceB0Mock_DoB0_Call) Return(interfaceB0MoqParam same_name_arg_and_type.interfaceB0) *interfaceB0Mock_DoB0_Call { +func (_c *interfaceB0Mock_DoB0_Call) Return(interfaceB0MoqParam interfaceB0) *interfaceB0Mock_DoB0_Call { _c.Call.Return(interfaceB0MoqParam) return _c } -func (_c *interfaceB0Mock_DoB0_Call) RunAndReturn(run func(interfaceB0 same_name_arg_and_type.interfaceB0)same_name_arg_and_type.interfaceB0) *interfaceB0Mock_DoB0_Call { +func (_c *interfaceB0Mock_DoB0_Call) RunAndReturn(run func(interfaceB01 interfaceB0)interfaceB0) *interfaceB0Mock_DoB0_Call { _c.Call.Return(run) return _c } diff --git a/pkg/fixtures/type_alias/mock_Interface1_test.go b/pkg/fixtures/type_alias/mock_Interface1_test.go index 44975f6b..9b506a56 100644 --- a/pkg/fixtures/type_alias/mock_Interface1_test.go +++ b/pkg/fixtures/type_alias/mock_Interface1_test.go @@ -5,7 +5,6 @@ package type_alias_test import ( - "github.com/vektra/mockery/v2/pkg/fixtures/type_alias" mock "github.com/stretchr/testify/mock" ) @@ -41,7 +40,7 @@ func (_m *Interface1) EXPECT() *Interface1_Expecter { // Foo provides a mock function for the type Interface1 -func (_mock *Interface1) Foo() type_alias.Type { +func (_mock *Interface1) Foo() Type { ret := _mock.Called() if len(ret) == 0 { @@ -49,11 +48,11 @@ func (_mock *Interface1) Foo() type_alias.Type { } - var r0 type_alias.Type - if returnFunc, ok := ret.Get(0).(func() type_alias.Type); ok { + var r0 Type + if returnFunc, ok := ret.Get(0).(func() Type); ok { r0 = returnFunc() } else { - r0 = ret.Get(0).(type_alias.Type) + r0 = ret.Get(0).(Type) } return r0 } @@ -79,12 +78,12 @@ func (_c *Interface1_Foo_Call) Run(run func()) *Interface1_Foo_Call { return _c } -func (_c *Interface1_Foo_Call) Return(v type_alias.Type) *Interface1_Foo_Call { +func (_c *Interface1_Foo_Call) Return(v Type) *Interface1_Foo_Call { _c.Call.Return(v) return _c } -func (_c *Interface1_Foo_Call) RunAndReturn(run func()type_alias.Type) *Interface1_Foo_Call { +func (_c *Interface1_Foo_Call) RunAndReturn(run func()Type) *Interface1_Foo_Call { _c.Call.Return(run) return _c } diff --git a/pkg/fixtures/type_alias/mock_Interface2_test.go b/pkg/fixtures/type_alias/mock_Interface2_test.go index 3c7409d6..0e084fb0 100644 --- a/pkg/fixtures/type_alias/mock_Interface2_test.go +++ b/pkg/fixtures/type_alias/mock_Interface2_test.go @@ -5,7 +5,6 @@ package type_alias_test import ( - "github.com/vektra/mockery/v2/pkg/fixtures/type_alias" "github.com/vektra/mockery/v2/pkg/fixtures/type_alias/subpkg" mock "github.com/stretchr/testify/mock" ) @@ -42,7 +41,7 @@ func (_m *Interface2) EXPECT() *Interface2_Expecter { // F provides a mock function for the type Interface2 -func (_mock *Interface2) F(v type_alias.Type, v1 type_alias.S, s subpkg.S) { _mock.Called(v, v1, s) +func (_mock *Interface2) F(v Type, v1 S, s subpkg.S) { _mock.Called(v, v1, s) return } @@ -63,9 +62,9 @@ func (_e *Interface2_Expecter) F(v interface{}, v1 interface{}, s interface{}, ) return &Interface2_F_Call{Call: _e.mock.On("F",v,v1,s, )} } -func (_c *Interface2_F_Call) Run(run func(v type_alias.Type, v1 type_alias.S, s subpkg.S)) *Interface2_F_Call { +func (_c *Interface2_F_Call) Run(run func(v Type, v1 S, s subpkg.S)) *Interface2_F_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(type_alias.Type),args[1].(type_alias.S),args[2].(subpkg.S),) + run(args[0].(Type),args[1].(S),args[2].(subpkg.S),) }) return _c } @@ -75,7 +74,7 @@ func (_c *Interface2_F_Call) Return() *Interface2_F_Call { return _c } -func (_c *Interface2_F_Call) RunAndReturn(run func(v type_alias.Type, v1 type_alias.S, s subpkg.S)) *Interface2_F_Call { +func (_c *Interface2_F_Call) RunAndReturn(run func(v Type, v1 S, s subpkg.S)) *Interface2_F_Call { _c.Run(run) return _c } diff --git a/pkg/registry/method_scope.go b/pkg/registry/method_scope.go index 3e6ab449..530caac1 100644 --- a/pkg/registry/method_scope.go +++ b/pkg/registry/method_scope.go @@ -19,7 +19,7 @@ type MethodScope struct { vars []*Var conflicted map[string]bool // visibleNames contains a collection of all names visible to this lexical - // scope. This includes import qualifiers. This is used to prevent naming + // scope. This includes import qualifiers, type names etc. This is used to prevent naming // collisions. visibleNames map[string]any imports map[string]*Package @@ -90,17 +90,21 @@ func (m *MethodScope) AddVar(ctx context.Context, vr *types.Var, prefix string) for key := range m.visibleNames { log.Debug().Str("visible-name", key).Msg("visible name") } - name := m.AllocateName(varName(vr, prefix)) - // This suggested name is subject to change because it might come into conflict - // with a future package import. - log.Debug().Str("suggested-name", name).Msg("suggested name for variable in method") v := Var{ vr: vr, imports: imports, moqPkgPath: m.moqPkgPath, - Name: name, } + // The variable type is also a visible name, so add that. + log.Debug().Str("type-string", v.TypeString()).Msg("VISIBLE NAME TYPE STRING") + m.visibleNames[v.TypeString()] = nil + name := m.AllocateName(varName(vr, prefix)) + // This suggested name is subject to change because it might come into conflict + // with a future package import. + log.Debug().Str("suggested-name", name).Msg("suggested name for variable in method") + + v.Name = name m.vars = append(m.vars, &v) return &v } diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go index 16de1b81..f7462142 100644 --- a/pkg/registry/registry.go +++ b/pkg/registry/registry.go @@ -73,11 +73,18 @@ func (r *Registry) MethodScope() *MethodScope { // suitable alias if there are any conflicts with previously imported // packages. func (r *Registry) AddImport(ctx context.Context, pkg *types.Package) *Package { - log := zerolog.Ctx(ctx) path := pkg.Path() - log.Debug().Str("method", "AddImport").Str("src-pkg-path", path).Str("dst-pkg-path", r.dstPkgPath).Msg("adding import") + log := zerolog.Ctx(ctx).With(). + Str("method", "AddImport"). + Str("src-pkg-path", path). + Str("dst-pkg-path", r.dstPkgPath). + Logger() + log.Debug().Msg("adding import") if path == r.dstPkgPath { + log.Debug().Msg("path equals dst-pkg-path, not adding import") return nil + } else { + log.Debug().Msg("path does not equal dst-pkg-path, adding import") } if imprt, ok := r.imports[path]; ok { diff --git a/pkg/template_generator.go b/pkg/template_generator.go index 6ebd3c40..587e29c6 100644 --- a/pkg/template_generator.go +++ b/pkg/template_generator.go @@ -4,10 +4,12 @@ import ( "bufio" "bytes" "context" + "errors" "fmt" "go/format" "go/token" "go/types" + "os" "strings" "github.com/chigopher/pathlib" @@ -27,6 +29,63 @@ const ( FORMAT_NOOP Formatter = "noop" ) +// findPkgPath returns the fully-qualified go import path of a given dir. The +// dir must be relative to a go.mod file. In the case it isn't, an error is returned. +func findPkgPath(dirPath *pathlib.Path) (string, error) { + if err := dirPath.MkdirAll(); err != nil { + return "", stackerr.NewStackErr(err) + } + dir, err := dirPath.ResolveAll() + if err != nil { + return "", stackerr.NewStackErr(err) + } + var goModFile *pathlib.Path + cursor := dir + for i := 0; ; i++ { + if i == 1000 { + return "", stackerr.NewStackErr(errors.New("failed to find go.mod after 1000 iterations")) + } + goMod := cursor.Join("go.mod") + goModExists, err := goMod.Exists() + if err != nil { + return "", stackerr.NewStackErr(err) + } + if !goModExists { + parent := cursor.Parent() + // Hit the root path + if cursor.String() == parent.String() { + return "", stackerr.NewStackErrf( + ErrGoModNotFound, "parsing package path for %s", dir.String()) + } + cursor = parent + continue + } + goModFile = goMod + break + } + dirRelative, err := dir.RelativeTo(goModFile.Parent()) + if err != nil { + return "", stackerr.NewStackErr(err) + } + fileBytes, err := goModFile.ReadFile() + if err != nil { + return "", stackerr.NewStackErr(err) + } + scanner := bufio.NewScanner(bytes.NewReader(fileBytes)) + // Iterate over each line + for scanner.Scan() { + if !strings.HasPrefix(scanner.Text(), "module") { + continue + } + moduleName := strings.Split(scanner.Text(), "module ")[1] + return pathlib.NewPath(moduleName, pathlib.PathWithSeperator("/")). + JoinPath(dirRelative). + Clean(). + String(), nil + } + return "", stackerr.NewStackErr(ErrGoModInvalid) +} + type TemplateGenerator struct { templateName string registry *registry.Registry @@ -36,23 +95,51 @@ type TemplateGenerator struct { } func NewTemplateGenerator( + ctx context.Context, srcPkg *packages.Package, - outPkgPath string, + outPkgFSPath *pathlib.Path, templateName string, formatter Formatter, pkgConfig *Config, ) (*TemplateGenerator, error) { + srcPkgFSPath := pathlib.NewPath(srcPkg.GoFiles[0]).Parent() + log := zerolog.Ctx(ctx).With(). + Stringer("srcPkgFSPath", srcPkgFSPath). + Stringer("outPkgFSPath", outPkgFSPath).Logger() + if !outPkgFSPath.IsAbsolute() { + cwd, err := os.Getwd() + if err != nil { + log.Err(err).Msg("failed to get current working directory") + return nil, stackerr.NewStackErr(err) + } + outPkgFSPath = pathlib.NewPath(cwd).JoinPath(outPkgFSPath) + } + outPkgPath, err := findPkgPath(outPkgFSPath) + if err != nil { + log.Err(err).Msg("failed to find output package path") + return nil, err + } + log = log.With().Str("outPkgPath", outPkgPath).Logger() reg, err := registry.New(srcPkg, outPkgPath) if err != nil { return nil, fmt.Errorf("creating new registry: %w", err) } + var inPackage bool + + if srcPkgFSPath.Equals(outPkgFSPath) { + log.Debug().Msg("output package detected to be in-package of original package") + inPackage = true + } else { + log.Debug().Msg("output package detected to not be in-package of original package") + } + return &TemplateGenerator{ templateName: templateName, registry: reg, formatter: formatter, pkgConfig: pkgConfig, - inPackage: srcPkg.PkgPath == outPkgPath, + inPackage: inPackage, }, nil } @@ -215,7 +302,7 @@ func (g *TemplateGenerator) Generate( return []byte{}, fmt.Errorf("executing template: %w", err) } - log.Debug().Msg("formatting file") + log.Debug().Msg("formatting file in-memory") // TODO: Grabbing ifaceConfigs[0].Formatter doesn't make sense. We should instead // grab the formatter as specified in the topmost interface-level config. formatted, err := g.format(buf.Bytes()) @@ -224,7 +311,7 @@ func (g *TemplateGenerator) Generate( for i := 1; scanner.Scan(); i++ { fmt.Printf("%d:\t%s\n", i, scanner.Text()) } - log.Err(err).Msg("can't format mock file") + log.Err(err).Msg("can't format mock file in-memory") return []byte{}, fmt.Errorf("formatting mock file: %w", err) } return formatted, nil