Skip to content

Commit

Permalink
Merge pull request vektra#218 from atombender/go-modules
Browse files Browse the repository at this point in the history
Support Go 1.11 modules
  • Loading branch information
evanphx authored Nov 16, 2018
2 parents ea26575 + 7576a96 commit 2be70d1
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 120 deletions.
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ language: go
go_import_path: github.com/vektra/mockery

go:
- 1.7.x
- 1.8.x
- 1.9.x
- "1.10.x"
- tip
Expand Down
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module github.com/vektra/mockery

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.1.1 // indirect
github.com/stretchr/testify v1.2.2
golang.org/x/tools v0.0.0-20181112210238-4b1f3b6b1646
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/tools v0.0.0-20181112210238-4b1f3b6b1646 h1:JEEoTsNEpPwxsebhPLC6P2jNr+6RFZLY4elUBVcMb+I=
golang.org/x/tools v0.0.0-20181112210238-4b1f3b6b1646/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
21 changes: 5 additions & 16 deletions mockery/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type GeneratorSuite struct {
}

func (s *GeneratorSuite) SetupTest() {
s.parser = NewParser()
s.parser = NewParser(nil)
}

func (s *GeneratorSuite) getInterfaceFromFile(interfacePath, interfaceName string) *Interface {
Expand All @@ -36,16 +36,15 @@ func (s *GeneratorSuite) getInterfaceFromFile(interfacePath, interfaceName strin
)

iface, err := s.parser.Find(interfaceName)
s.NoError(err, "The requested interface was found.")
s.Require().NoError(err)
s.Require().NotNil(iface)
return iface
}

func (s *GeneratorSuite) getGenerator(
filepath, interfaceName string, inPackage bool,
) *Generator {
return NewGenerator(
s.getInterfaceFromFile(filepath, interfaceName), pkg, inPackage,
)
return NewGenerator(s.getInterfaceFromFile(filepath, interfaceName), pkg, inPackage)
}

func (s *GeneratorSuite) checkGeneration(
Expand Down Expand Up @@ -85,14 +84,6 @@ func (s *GeneratorSuite) checkPrologueGeneration(
)
}

func (s *GeneratorSuite) getInterfaceRelPath(iface *Interface) string {
local, err := filepath.Rel(getGoPathSrc(), filepath.Dir(iface.Path))
s.NoError(err, "No errors with relative path generation.")

// Align w/ Generator.getLocalizedPath and enforce '/' slashes for import paths in every OS.
return filepath.ToSlash(local)
}

func (s *GeneratorSuite) TestCalculateImport() {
gp := []string{"a/src", "b/src"}

Expand Down Expand Up @@ -1014,15 +1005,13 @@ func (s *GeneratorSuite) TestPrologueWithImportSameAsLocalPackage() {
generator := s.getGenerator(
"imports_same_as_package.go", "ImportsSameAsPackage", false,
)
s.getInterfaceRelPath(generator.iface)
expected := `package mocks
import fixtures "` + s.getInterfaceRelPath(generator.iface) + `"
import fixtures "` + generator.iface.Path + `"
import mock "github.com/stretchr/testify/mock"
import test "github.com/vektra/mockery/mockery/fixtures/test"
`

s.checkPrologueGeneration(generator, expected)
}

Expand Down
169 changes: 80 additions & 89 deletions mockery/parse.go
Original file line number Diff line number Diff line change
@@ -1,54 +1,47 @@
package mockery

import (
"fmt"
"go/ast"
"go/build"
"go/importer"
"go/types"
"io/ioutil"
"path/filepath"
"sort"
"strings"
"sync"

"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/packages"
)

type Parser struct {
configMapping map[string][]*ast.File
pathToInterfaces map[string][]string
pathToASTFile map[string]*ast.File
parserPackages []*types.Package
conf loader.Config
type parserEntry struct {
fileName string
pkg *packages.Package
syntax *ast.File
interfaces []string
}

func NewParser() *Parser {
var conf loader.Config

conf.TypeCheckFuncBodies = func(_ string) bool { return false }
conf.TypeChecker.DisableUnusedImportCheck = true
conf.TypeChecker.Importer = importer.Default()

// Initialize the build context (e.g. GOARCH/GOOS fields) so we can use it for respecting
// build tags during Parse.
buildCtx := build.Default
conf.Build = &buildCtx

type Parser struct {
entries []*parserEntry
entriesByFileName map[string]*parserEntry
packages []*packages.Package
parserPackages []*types.Package
conf packages.Config
}

func NewParser(buildTags []string) *Parser {
var conf packages.Config
conf.Mode = packages.LoadSyntax
if len(buildTags) > 0 {
conf.BuildFlags = []string{"-tags", strings.Join(buildTags, ",")}
}
return &Parser{
parserPackages: make([]*types.Package, 0),
configMapping: make(map[string][]*ast.File),
pathToInterfaces: make(map[string][]string),
pathToASTFile: make(map[string]*ast.File),
conf: conf,
parserPackages: make([]*types.Package, 0),
entriesByFileName: map[string]*parserEntry{},
conf: conf,
}
}

func (p *Parser) AddBuildTags(buildTags ...string) {
p.conf.Build.BuildTags = append(p.conf.Build.BuildTags, buildTags...)
}

func (p *Parser) Parse(path string) error {

// To support relative paths to mock targets w/ vendor deps, we need to provide eventual
// calls to build.Context.Import with an absolute path. It needs to be absolute because
// Import will only find the vendor directory if our target path for parsing is under
Expand All @@ -75,27 +68,46 @@ func (p *Parser) Parse(path string) error {

fname := fi.Name()
fpath := filepath.Join(dir, fname)
if _, ok := p.entriesByFileName[fpath]; ok {
continue
}

// If go/build would ignore this file, e.g. based on build tags, also ignore it here.
//
// (Further coupling with go internals and x/tools may of course bear a cost eventually
// e.g. https://github.com/vektra/mockery/pull/117#issue-199337071, but should add
// worthwhile consistency in this tool's behavior in the meantime.)
match, matchErr := p.conf.Build.MatchFile(dir, fname)
if matchErr != nil {
return matchErr
pkgs, err := packages.Load(&p.conf, "file="+fpath)
if err != nil {
return err
}
if !match {
if len(pkgs) == 0 {
continue
}
if len(pkgs) > 1 {
names := make([]string, len(pkgs))
for i, p := range pkgs {
names[i] = p.Name
}
panic(fmt.Sprintf("file %s resolves to multiple packages: %s", fpath, strings.Join(names, ", ")))
}

f, parseErr := p.conf.ParseFile(fpath, nil)
if parseErr != nil {
return parseErr
pkg := pkgs[0]
if len(pkg.Errors) > 0 {
return pkg.Errors[0]
}
if len(pkg.GoFiles) == 0 {
continue
}

p.configMapping[path] = append(p.configMapping[path], f)
p.pathToASTFile[fpath] = f
for idx, f := range pkg.GoFiles {
if _, ok := p.entriesByFileName[f]; ok {
continue
}
entry := parserEntry{
fileName: f,
pkg: pkg,
syntax: pkg.Syntax[idx],
}
p.entries = append(p.entries, &entry)
p.entriesByFileName[f] = &entry
}
p.packages = append(p.packages, pkg)
}

return nil
Expand Down Expand Up @@ -129,61 +141,35 @@ func (p *Parser) Load() error {
var wg sync.WaitGroup
wg.Add(1)
go func() {
for path, fi := range p.pathToASTFile {
for _, entry := range p.entries {
nv := NewNodeVisitor()
ast.Walk(nv, fi)
p.pathToInterfaces[path] = nv.DeclaredInterfaces()
ast.Walk(nv, entry.syntax)
entry.interfaces = nv.DeclaredInterfaces()
}
wg.Done()
}()

// Type-check a package consisting of this file.
// Type information for the imported packages
// comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
for path, files := range p.configMapping {
p.conf.CreateFromFiles(path, files...)
}

prog, err := p.conf.Load()
if err != nil {
return err
}

for _, pkgInfo := range prog.Created {
p.parserPackages = append(p.parserPackages, pkgInfo.Pkg)
}

wg.Wait()
return nil
}

func (p *Parser) Find(name string) (*Interface, error) {
for _, pkg := range p.parserPackages {
if iface := p.FindInPackage(name, pkg); iface != nil {
return iface, nil
for _, entry := range p.entries {
for _, iface := range entry.interfaces {
if iface == name {
list := p.packageInterfaces(entry.pkg.Types, entry.syntax, entry.fileName, []string{name}, nil)
if len(list) > 0 {
return list[0], nil
}
}
}
}
return nil, ErrNotInterface
}

func (p *Parser) FindInPackage(name string, pkg *types.Package) *Interface {
iFaces := p.pathToInterfaces[pkg.Path()]
for i := 0; i < len(iFaces); i++ {
iface := iFaces[i]
if iface == name {
list := make([]*Interface, 0)
file := p.pathToASTFile[pkg.Path()]
list = p.packageInterfaces(pkg, file, []string{name}, list)
return list[0]
}
}

return nil
}

type Interface struct {
Name string
Path string
FileName string
File *ast.File
Pkg *types.Package
Type *types.Interface
Expand All @@ -206,18 +192,22 @@ func (s sortableIFaceList) Less(i, j int) bool {

func (p *Parser) Interfaces() []*Interface {
ifaces := make(sortableIFaceList, 0)
for _, pkg := range p.parserPackages {
path := pkg.Path()
declaredIfaces := p.pathToInterfaces[path]
astFile := p.pathToASTFile[path]
ifaces = p.packageInterfaces(pkg, astFile, declaredIfaces, ifaces)
for _, entry := range p.entries {
declaredIfaces := entry.interfaces
astFile := entry.syntax
ifaces = p.packageInterfaces(entry.pkg.Types, astFile, entry.fileName, declaredIfaces, ifaces)
}

sort.Sort(ifaces)
return ifaces
}

func (p *Parser) packageInterfaces(pkg *types.Package, file *ast.File, declaredInterfaces []string, ifaces []*Interface) []*Interface {
func (p *Parser) packageInterfaces(
pkg *types.Package,
file *ast.File,
fileName string,
declaredInterfaces []string,
ifaces []*Interface) []*Interface {
scope := pkg.Scope()
for _, name := range declaredInterfaces {
obj := scope.Lookup(name)
Expand All @@ -244,6 +234,7 @@ func (p *Parser) packageInterfaces(pkg *types.Package, file *ast.File, declaredI
Name: name,
Pkg: pkg,
Path: pkg.Path(),
FileName: fileName,
Type: iface.Complete(),
NamedType: typ,
File: file,
Expand Down
Loading

0 comments on commit 2be70d1

Please sign in to comment.