diff --git a/go.sum b/go.sum index 21dbce53..0549f947 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/mockgen/internal/tests/import_embedded_interface/bugreport.go b/mockgen/internal/tests/import_embedded_interface/bugreport.go new file mode 100644 index 00000000..c6d73f0f --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/bugreport.go @@ -0,0 +1,35 @@ +// Copyright 2020 Google Inc. +// +// 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:generate mockgen -destination bugreport_mock.go -package bugreport -source=bugreport.go + +package bugreport + +import ( + "log" + + "github.com/golang/mock/mockgen/internal/tests/import_embedded_interface/ersatz" + "github.com/golang/mock/mockgen/internal/tests/import_embedded_interface/faux" +) + +// Source is an interface w/ an embedded foreign interface +type Source interface { + ersatz.Embedded + faux.Foreign +} + +func CallForeignMethod(s Source) { + log.Println(s.Ersatz()) + log.Println(s.OtherErsatz()) +} diff --git a/mockgen/internal/tests/import_embedded_interface/bugreport_mock.go b/mockgen/internal/tests/import_embedded_interface/bugreport_mock.go new file mode 100644 index 00000000..c618497a --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/bugreport_mock.go @@ -0,0 +1,63 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: bugreport.go + +// Package bugreport is a generated GoMock package. +package bugreport + +import ( + gomock "github.com/golang/mock/gomock" + ersatz "github.com/golang/mock/mockgen/internal/tests/import_embedded_interface/ersatz" + ersatz0 "github.com/golang/mock/mockgen/internal/tests/import_embedded_interface/other/ersatz" + reflect "reflect" +) + +// MockSource is a mock of Source interface +type MockSource struct { + ctrl *gomock.Controller + recorder *MockSourceMockRecorder +} + +// MockSourceMockRecorder is the mock recorder for MockSource +type MockSourceMockRecorder struct { + mock *MockSource +} + +// NewMockSource creates a new mock instance +func NewMockSource(ctrl *gomock.Controller) *MockSource { + mock := &MockSource{ctrl: ctrl} + mock.recorder = &MockSourceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockSource) EXPECT() *MockSourceMockRecorder { + return m.recorder +} + +// Ersatz mocks base method +func (m *MockSource) Ersatz() ersatz.Return { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Ersatz") + ret0, _ := ret[0].(ersatz.Return) + return ret0 +} + +// Ersatz indicates an expected call of Ersatz +func (mr *MockSourceMockRecorder) Ersatz() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ersatz", reflect.TypeOf((*MockSource)(nil).Ersatz)) +} + +// OtherErsatz mocks base method +func (m *MockSource) OtherErsatz() ersatz0.Return { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OtherErsatz") + ret0, _ := ret[0].(ersatz0.Return) + return ret0 +} + +// OtherErsatz indicates an expected call of OtherErsatz +func (mr *MockSourceMockRecorder) OtherErsatz() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OtherErsatz", reflect.TypeOf((*MockSource)(nil).OtherErsatz)) +} diff --git a/mockgen/internal/tests/import_embedded_interface/bugreport_test.go b/mockgen/internal/tests/import_embedded_interface/bugreport_test.go new file mode 100644 index 00000000..5915cbe1 --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/bugreport_test.go @@ -0,0 +1,31 @@ +// Copyright 2020 Google Inc. +// +// 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. +package bugreport + +import ( + "testing" + + "github.com/golang/mock/gomock" +) + +// TestValidInterface assesses whether or not the generated mock is valid +func TestValidInterface(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + s := NewMockSource(ctrl) + s.EXPECT().Ersatz().Return("") + s.EXPECT().OtherErsatz().Return("") + CallForeignMethod(s) +} diff --git a/mockgen/internal/tests/import_embedded_interface/ersatz/ersatz.go b/mockgen/internal/tests/import_embedded_interface/ersatz/ersatz.go new file mode 100644 index 00000000..0d2c7815 --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/ersatz/ersatz.go @@ -0,0 +1,20 @@ +// Copyright 2020 Google Inc. +// +// 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. +package ersatz + +type Embedded interface { + Ersatz() Return +} + +type Return interface{} diff --git a/mockgen/internal/tests/import_embedded_interface/faux/conflict.go b/mockgen/internal/tests/import_embedded_interface/faux/conflict.go new file mode 100644 index 00000000..27803dd6 --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/faux/conflict.go @@ -0,0 +1,20 @@ +// Copyright 2020 Google Inc. +// +// 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. +package faux + +import "github.com/golang/mock/mockgen/internal/tests/import_embedded_interface/other/log" + +func Conflict1() { + log.Foo() +} diff --git a/mockgen/internal/tests/import_embedded_interface/faux/faux.go b/mockgen/internal/tests/import_embedded_interface/faux/faux.go new file mode 100644 index 00000000..e1d66988 --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/faux/faux.go @@ -0,0 +1,28 @@ +// Copyright 2020 Google Inc. +// +// 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. +package faux + +import ( + "log" + + "github.com/golang/mock/mockgen/internal/tests/import_embedded_interface/other/ersatz" +) + +type Foreign interface { + ersatz.Embedded +} + +func Conflict0() { + log.Println() +} diff --git a/mockgen/internal/tests/import_embedded_interface/net.go b/mockgen/internal/tests/import_embedded_interface/net.go new file mode 100644 index 00000000..38fbd039 --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/net.go @@ -0,0 +1,25 @@ +// +// 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:generate mockgen -destination net_mock.go -package bugreport -source=net.go +package bugreport + +import "net/http" + +type Net interface { + http.ResponseWriter +} + +func CallResponseWriterMethods(n Net) { + n.WriteHeader(10) +} diff --git a/mockgen/internal/tests/import_embedded_interface/net_mock.go b/mockgen/internal/tests/import_embedded_interface/net_mock.go new file mode 100644 index 00000000..50ec11ec --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/net_mock.go @@ -0,0 +1,75 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: net.go + +// Package bugreport is a generated GoMock package. +package bugreport + +import ( + gomock "github.com/golang/mock/gomock" + http "net/http" + reflect "reflect" +) + +// MockNet is a mock of Net interface +type MockNet struct { + ctrl *gomock.Controller + recorder *MockNetMockRecorder +} + +// MockNetMockRecorder is the mock recorder for MockNet +type MockNetMockRecorder struct { + mock *MockNet +} + +// NewMockNet creates a new mock instance +func NewMockNet(ctrl *gomock.Controller) *MockNet { + mock := &MockNet{ctrl: ctrl} + mock.recorder = &MockNetMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockNet) EXPECT() *MockNetMockRecorder { + return m.recorder +} + +// Header mocks base method +func (m *MockNet) Header() http.Header { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Header") + ret0, _ := ret[0].(http.Header) + return ret0 +} + +// Header indicates an expected call of Header +func (mr *MockNetMockRecorder) Header() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockNet)(nil).Header)) +} + +// Write mocks base method +func (m *MockNet) Write(arg0 []byte) (int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Write", arg0) + ret0, _ := ret[0].(int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Write indicates an expected call of Write +func (mr *MockNetMockRecorder) Write(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockNet)(nil).Write), arg0) +} + +// WriteHeader mocks base method +func (m *MockNet) WriteHeader(statusCode int) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "WriteHeader", statusCode) +} + +// WriteHeader indicates an expected call of WriteHeader +func (mr *MockNetMockRecorder) WriteHeader(statusCode interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteHeader", reflect.TypeOf((*MockNet)(nil).WriteHeader), statusCode) +} diff --git a/mockgen/internal/tests/import_embedded_interface/net_test.go b/mockgen/internal/tests/import_embedded_interface/net_test.go new file mode 100644 index 00000000..381f7de3 --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/net_test.go @@ -0,0 +1,30 @@ +// Copyright 2020 Google Inc. +// +// 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. +package bugreport + +import ( + "testing" + + "github.com/golang/mock/gomock" +) + +// TestValidInterface assesses whether or not the generated mock is valid +func TestValidNetInterface(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + s := NewMockNet(ctrl) + s.EXPECT().WriteHeader(10) + CallResponseWriterMethods(s) +} diff --git a/mockgen/internal/tests/import_embedded_interface/other/ersatz/ersatz.go b/mockgen/internal/tests/import_embedded_interface/other/ersatz/ersatz.go new file mode 100644 index 00000000..9ca63914 --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/other/ersatz/ersatz.go @@ -0,0 +1,20 @@ +// Copyright 2020 Google Inc. +// +// 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. +package ersatz + +type Embedded interface { + OtherErsatz() Return +} + +type Return interface{} diff --git a/mockgen/internal/tests/import_embedded_interface/other/log/log.go b/mockgen/internal/tests/import_embedded_interface/other/log/log.go new file mode 100644 index 00000000..c0d032bf --- /dev/null +++ b/mockgen/internal/tests/import_embedded_interface/other/log/log.go @@ -0,0 +1,16 @@ +// Copyright 2020 Google Inc. +// +// 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. +package log + +func Foo() {} diff --git a/mockgen/parse.go b/mockgen/parse.go index a8edde80..7840eed6 100644 --- a/mockgen/parse.go +++ b/mockgen/parse.go @@ -62,7 +62,7 @@ func sourceMode(source string) (*model.Package, error) { p := &fileParser{ fileSet: fs, - imports: make(map[string]string), + imports: make(map[string]importedPackage), importedInterfaces: make(map[string]map[string]*ast.InterfaceType), auxInterfaces: make(map[string]map[string]*ast.InterfaceType), srcDir: srcDir, @@ -79,7 +79,7 @@ func sourceMode(source string) (*model.Package, error) { dotImports[v] = true } else { // TODO: Catch dupes? - p.imports[k] = v + p.imports[k] = importedPkg{path: v} } } } @@ -100,9 +100,39 @@ func sourceMode(source string) (*model.Package, error) { return pkg, nil } +type importedPackage interface { + Path() string + Parser() *fileParser +} + +type importedPkg struct { + path string + parser *fileParser +} + +func (i importedPkg) Path() string { return i.path } +func (i importedPkg) Parser() *fileParser { return i.parser } + +// duplicateImport is a bit of a misnomer. Currently the parser can't +// handle cases of multi-file packages importing different packages +// under the same name. Often these imports would not be problematic, +// so this type lets us defer raising an error unless the package name +// is actually used. +type duplicateImport struct { + name string + duplicates []string +} + +func (d duplicateImport) Error() string { + return fmt.Sprintf("%q is ambigous because of duplicate imports: %v", d.name, d.duplicates) +} + +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]string // package name => import path + imports map[string]importedPackage // package name => imported package importedInterfaces map[string]map[string]*ast.InterfaceType // package (or "") => name => interface auxFiles []*ast.File @@ -154,18 +184,18 @@ func (p *fileParser) addAuxInterfacesFromFile(pkg string, file *ast.File) { func (p *fileParser) parseFile(importPath string, file *ast.File) (*model.Package, error) { allImports, dotImports := importsOfFile(file) // Don't stomp imports provided by -imports. Those should take precedence. - for pkg, pkgPath := range allImports { + for pkg, pkgI := range allImports { if _, ok := p.imports[pkg]; !ok { - p.imports[pkg] = pkgPath + p.imports[pkg] = pkgI } } // Add imports from auxiliary files, which might be needed for embedded interfaces. // Don't stomp any other imports. for _, f := range p.auxFiles { auxImports, _ := importsOfFile(f) - for pkg, pkgPath := range auxImports { + for pkg, pkgI := range auxImports { if _, ok := p.imports[pkg]; !ok { - p.imports[pkg] = pkgPath + p.imports[pkg] = pkgI } } } @@ -186,31 +216,38 @@ func (p *fileParser) parseFile(importPath string, file *ast.File) (*model.Packag }, nil } -// parsePackage loads package specified by path, parses it and populates -// corresponding imports and importedInterfaces into the fileParser. -func (p *fileParser) parsePackage(path string) error { +// parsePackage loads package specified by path, parses it and returns +// a new fileParser with the parsed imports and interfaces. +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), + srcDir: p.srcDir, + } + var pkgs map[string]*ast.Package - if imp, err := build.Import(path, p.srcDir, build.FindOnly); err != nil { - return err - } else if pkgs, err = parser.ParseDir(p.fileSet, imp.Dir, nil, 0); err != nil { - return err + if imp, err := build.Import(path, newP.srcDir, build.FindOnly); err != nil { + return nil, err + } else if pkgs, err = parser.ParseDir(newP.fileSet, imp.Dir, nil, 0); err != nil { + return nil, err } + for _, pkg := range pkgs { file := ast.MergePackageFiles(pkg, ast.FilterFuncDuplicates|ast.FilterUnassociatedComments|ast.FilterImportDuplicates) - if _, ok := p.importedInterfaces[path]; !ok { - p.importedInterfaces[path] = make(map[string]*ast.InterfaceType) + if _, ok := newP.importedInterfaces[path]; !ok { + newP.importedInterfaces[path] = make(map[string]*ast.InterfaceType) } for ni := range iterInterfaces(file) { - p.importedInterfaces[path][ni.name.Name] = ni.it + newP.importedInterfaces[path][ni.name.Name] = ni.it } imports, _ := importsOfFile(file) - for pkgName, pkgPath := range imports { - if _, ok := p.imports[pkgName]; !ok { - p.imports[pkgName] = pkgPath - } + for pkgName, pkgI := range imports { + newP.imports[pkgName] = pkgI } } - return nil + return newP, nil } func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*model.Interface, error) { @@ -252,21 +289,36 @@ func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*m if !ok { return nil, p.errorf(v.X.Pos(), "unknown package %s", fpkg) } + + var eintf *model.Interface + var err error ei := p.auxInterfaces[fpkg][sel] - if ei == nil { - fpkg = epkg - if _, ok = p.importedInterfaces[epkg]; !ok { - if err := p.parsePackage(epkg); err != nil { - return nil, p.errorf(v.Pos(), "could not parse package %s: %v", fpkg, err) + if ei != nil { + eintf, err = p.parseInterface(sel, fpkg, ei) + if err != nil { + return nil, err + } + } else { + path := epkg.Path() + parser := epkg.Parser() + if parser == nil { + ip, err := p.parsePackage(path) + if err != nil { + return nil, p.errorf(v.Pos(), "could not parse package %s: %v", path, err) + } + parser = ip + p.imports[fpkg] = importedPkg{ + path: epkg.Path(), + parser: parser, } } - if ei = p.importedInterfaces[epkg][sel]; ei == nil { - return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", fpkg, sel) + if ei = parser.importedInterfaces[path][sel]; ei == nil { + return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", path, sel) + } + eintf, err = parser.parseInterface(sel, path, ei) + if err != nil { + return nil, err } - } - eintf, err := p.parseInterface(sel, fpkg, ei) - if err != nil { - return nil, err } // Copy the methods. // TODO: apply shadowing rules. @@ -383,7 +435,7 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) { // if so, patch the import w/ the fully qualified import maybeImportedPkg, ok := p.imports[pkg] if ok { - pkg = maybeImportedPkg + pkg = maybeImportedPkg.Path() } // assume type in this package return &model.NamedType{Package: pkg, Type: v.Name}, nil @@ -412,7 +464,7 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) { if !ok { return nil, p.errorf(v.Pos(), "unknown package %q", pkgName) } - return &model.NamedType{Package: pkg, Type: v.Sel.String()}, nil + return &model.NamedType{Package: pkg.Path(), Type: v.Sel.String()}, nil case *ast.StarExpr: t, err := p.parseType(pkg, v.X) if err != nil { @@ -431,7 +483,7 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) { // importsOfFile returns a map of package name to import path // of the imports in file. -func importsOfFile(file *ast.File) (normalImports map[string]string, dotImports []string) { +func importsOfFile(file *ast.File) (normalImports map[string]importedPackage, dotImports []string) { var importPaths []string for _, is := range file.Imports { if is.Name != nil { @@ -441,7 +493,7 @@ func importsOfFile(file *ast.File) (normalImports map[string]string, dotImports importPaths = append(importPaths, importPath) } packagesName := createPackageMap(importPaths) - normalImports = make(map[string]string) + normalImports = make(map[string]importedPackage) dotImports = make([]string, 0) for _, is := range file.Imports { var pkgName string @@ -469,11 +521,22 @@ func importsOfFile(file *ast.File) (normalImports map[string]string, dotImports if pkgName == "." { dotImports = append(dotImports, importPath) } else { - - if _, ok := normalImports[pkgName]; ok { - log.Fatalf("imported package collision: %q imported twice", pkgName) + if pkg, ok := normalImports[pkgName]; ok { + switch p := pkg.(type) { + case duplicateImport: + normalImports[pkgName] = duplicateImport{ + name: p.name, + duplicates: append([]string{importPath}, p.duplicates...), + } + case importedPkg: + normalImports[pkgName] = duplicateImport{ + name: pkgName, + duplicates: []string{p.path, importPath}, + } + } + } else { + normalImports[pkgName] = importedPkg{path: importPath} } - normalImports[pkgName] = importPath } } return diff --git a/mockgen/parse_test.go b/mockgen/parse_test.go index dab22fa5..e555b45e 100644 --- a/mockgen/parse_test.go +++ b/mockgen/parse_test.go @@ -16,7 +16,7 @@ func TestFileParser_ParseFile(t *testing.T) { p := fileParser{ fileSet: fs, - imports: make(map[string]string), + imports: make(map[string]importedPackage), importedInterfaces: make(map[string]map[string]*ast.InterfaceType), } @@ -47,16 +47,16 @@ func TestFileParser_ParsePackage(t *testing.T) { p := fileParser{ fileSet: fs, - imports: make(map[string]string), + imports: make(map[string]importedPackage), importedInterfaces: make(map[string]map[string]*ast.InterfaceType), } - err = p.parsePackage("github.com/golang/mock/mockgen/internal/tests/custom_package_name/greeter") + newP, err := p.parsePackage("github.com/golang/mock/mockgen/internal/tests/custom_package_name/greeter") if err != nil { t.Fatalf("Unexpected error: %v", err) } - checkGreeterImports(t, p.imports) + checkGreeterImports(t, newP.imports) } func TestImportsOfFile(t *testing.T) { @@ -70,14 +70,14 @@ func TestImportsOfFile(t *testing.T) { checkGreeterImports(t, imports) } -func checkGreeterImports(t *testing.T, imports map[string]string) { +func checkGreeterImports(t *testing.T, imports map[string]importedPackage) { // check that imports have stdlib package "fmt" if fmtPackage, ok := imports["fmt"]; !ok { t.Errorf("Expected imports to have key \"fmt\"") } else { expectedFmtPackage := "fmt" - if fmtPackage != expectedFmtPackage { - t.Errorf("Expected fmt key to have value %s but got %s", expectedFmtPackage, fmtPackage) + if fmtPackage.Path() != expectedFmtPackage { + t.Errorf("Expected fmt key to have value %s but got %s", expectedFmtPackage, fmtPackage.Path()) } } @@ -86,8 +86,8 @@ func checkGreeterImports(t *testing.T, imports map[string]string) { t.Errorf("Expected imports to have key \"fmt\"") } else { expectedValidatorPackage := "github.com/golang/mock/mockgen/internal/tests/custom_package_name/validator" - if validatorPackage != expectedValidatorPackage { - t.Errorf("Expected validator key to have value %s but got %s", expectedValidatorPackage, validatorPackage) + if validatorPackage.Path() != expectedValidatorPackage { + t.Errorf("Expected validator key to have value %s but got %s", expectedValidatorPackage, validatorPackage.Path()) } } @@ -96,8 +96,8 @@ func checkGreeterImports(t *testing.T, imports map[string]string) { t.Errorf("Expected imports to have key \"client\"") } else { expectedClientPackage := "github.com/golang/mock/mockgen/internal/tests/custom_package_name/client/v1" - if clientPackage != expectedClientPackage { - t.Errorf("Expected client key to have value %s but got %s", expectedClientPackage, clientPackage) + if clientPackage.Path() != expectedClientPackage { + t.Errorf("Expected client key to have value %s but got %s", expectedClientPackage, clientPackage.Path()) } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy