mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-28 08:19:58 +00:00
TUN-8006: Update quic-go to latest upstream
This commit is contained in:
116
vendor/go.uber.org/mock/mockgen/generic_go118.go
generated
vendored
Normal file
116
vendor/go.uber.org/mock/mockgen/generic_go118.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/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]model.Type) (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 any) 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()
|
||||
}
|
||||
|
||||
func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) {
|
||||
var indices []ast.Expr
|
||||
var typ ast.Expr
|
||||
switch v := field.Type.(type) {
|
||||
case *ast.IndexExpr:
|
||||
indices = []ast.Expr{v.Index}
|
||||
typ = v.X
|
||||
case *ast.IndexListExpr:
|
||||
indices = v.Indices
|
||||
typ = v.X
|
||||
default:
|
||||
return nil, fmt.Errorf("don't know how to mock method of type %T", field.Type)
|
||||
}
|
||||
|
||||
nf := &ast.Field{
|
||||
Doc: field.Comment,
|
||||
Names: field.Names,
|
||||
Type: typ,
|
||||
Tag: field.Tag,
|
||||
Comment: field.Comment,
|
||||
}
|
||||
|
||||
it.embeddedInstTypeParams = indices
|
||||
|
||||
return p.parseMethod(nf, it, iface, pkg, tps)
|
||||
}
|
41
vendor/go.uber.org/mock/mockgen/generic_notgo118.go
generated
vendored
Normal file
41
vendor/go.uber.org/mock/mockgen/generic_notgo118.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"go.uber.org/mock/mockgen/model"
|
||||
)
|
||||
|
||||
func getTypeSpecTypeParams(ts *ast.TypeSpec) []*ast.Field {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fileParser) parseGenericType(pkg string, typ ast.Expr, tps map[string]model.Type) (model.Type, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func getIdentTypeParams(decl any) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) {
|
||||
return nil, fmt.Errorf("don't know how to mock method of type %T", field.Type)
|
||||
}
|
870
vendor/go.uber.org/mock/mockgen/mockgen.go
generated
vendored
Normal file
870
vendor/go.uber.org/mock/mockgen/mockgen.go
generated
vendored
Normal file
@@ -0,0 +1,870 @@
|
||||
// Copyright 2010 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.
|
||||
|
||||
// MockGen generates mock implementations of Go interfaces.
|
||||
package main
|
||||
|
||||
// TODO: This does not support recursive embedded interfaces.
|
||||
// TODO: This does not support embedding package-local interfaces in a separate file.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/mod/modfile"
|
||||
toolsimports "golang.org/x/tools/imports"
|
||||
|
||||
"go.uber.org/mock/mockgen/model"
|
||||
)
|
||||
|
||||
const (
|
||||
gomockImportPath = "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
var (
|
||||
version = ""
|
||||
commit = "none"
|
||||
date = "unknown"
|
||||
)
|
||||
|
||||
var (
|
||||
source = flag.String("source", "", "(source mode) Input Go source file; enables source mode.")
|
||||
destination = flag.String("destination", "", "Output file; defaults to stdout.")
|
||||
mockNames = flag.String("mock_names", "", "Comma-separated interfaceName=mockName pairs of explicit mock names to use. Mock names default to 'Mock'+ interfaceName suffix.")
|
||||
packageOut = flag.String("package", "", "Package of the generated code; defaults to the package of the input with a 'mock_' prefix.")
|
||||
selfPackage = flag.String("self_package", "", "The full package import path for the generated code. The purpose of this flag is to prevent import cycles in the generated code by trying to include its own package. This can happen if the mock's package is set to one of its inputs (usually the main one) and the output is stdio so mockgen cannot detect the final output package. Setting this flag will then tell mockgen which import to exclude.")
|
||||
writePkgComment = flag.Bool("write_package_comment", true, "Writes package documentation comment (godoc) if true.")
|
||||
writeSourceComment = flag.Bool("write_source_comment", true, "Writes original file (source mode) or interface names (reflect mode) comment if true.")
|
||||
writeGenerateDirective = flag.Bool("write_generate_directive", false, "Add //go:generate directive to regenerate the mock")
|
||||
copyrightFile = flag.String("copyright_file", "", "Copyright file used to add copyright header")
|
||||
typed = flag.Bool("typed", false, "Generate Type-safe 'Return', 'Do', 'DoAndReturn' function")
|
||||
imports = flag.String("imports", "", "(source mode) Comma-separated name=path pairs of explicit imports to use.")
|
||||
auxFiles = flag.String("aux_files", "", "(source mode) Comma-separated pkg=path pairs of auxiliary Go source files.")
|
||||
excludeInterfaces = flag.String("exclude_interfaces", "", "Comma-separated names of interfaces to be excluded")
|
||||
|
||||
debugParser = flag.Bool("debug_parser", false, "Print out parser results only.")
|
||||
showVersion = flag.Bool("version", false, "Print version.")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if *showVersion {
|
||||
printVersion()
|
||||
return
|
||||
}
|
||||
|
||||
var pkg *model.Package
|
||||
var err error
|
||||
var packageName string
|
||||
if *source != "" {
|
||||
pkg, err = sourceMode(*source)
|
||||
} else {
|
||||
if flag.NArg() != 2 {
|
||||
usage()
|
||||
log.Fatal("Expected exactly two arguments")
|
||||
}
|
||||
packageName = flag.Arg(0)
|
||||
interfaces := strings.Split(flag.Arg(1), ",")
|
||||
if packageName == "." {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Get current directory failed: %v", err)
|
||||
}
|
||||
packageName, err = packageNameOfDir(dir)
|
||||
if err != nil {
|
||||
log.Fatalf("Parse package name failed: %v", err)
|
||||
}
|
||||
}
|
||||
pkg, err = reflectMode(packageName, interfaces)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Loading input failed: %v", err)
|
||||
}
|
||||
|
||||
if *debugParser {
|
||||
pkg.Print(os.Stdout)
|
||||
return
|
||||
}
|
||||
|
||||
outputPackageName := *packageOut
|
||||
if outputPackageName == "" {
|
||||
// pkg.Name in reflect mode is the base name of the import path,
|
||||
// which might have characters that are illegal to have in package names.
|
||||
outputPackageName = "mock_" + sanitize(pkg.Name)
|
||||
}
|
||||
|
||||
// outputPackagePath represents the fully qualified name of the package of
|
||||
// the generated code. Its purposes are to prevent the module from importing
|
||||
// itself and to prevent qualifying type names that come from its own
|
||||
// package (i.e. if there is a type called X then we want to print "X" not
|
||||
// "package.X" since "package" is this package). This can happen if the mock
|
||||
// is output into an already existing package.
|
||||
outputPackagePath := *selfPackage
|
||||
if outputPackagePath == "" && *destination != "" {
|
||||
dstPath, err := filepath.Abs(filepath.Dir(*destination))
|
||||
if err == nil {
|
||||
pkgPath, err := parsePackageImport(dstPath)
|
||||
if err == nil {
|
||||
outputPackagePath = pkgPath
|
||||
} else {
|
||||
log.Println("Unable to infer -self_package from destination file path:", err)
|
||||
}
|
||||
} else {
|
||||
log.Println("Unable to determine destination file path:", err)
|
||||
}
|
||||
}
|
||||
|
||||
g := new(generator)
|
||||
if *source != "" {
|
||||
g.filename = *source
|
||||
} else {
|
||||
g.srcPackage = packageName
|
||||
g.srcInterfaces = flag.Arg(1)
|
||||
}
|
||||
g.destination = *destination
|
||||
|
||||
if *mockNames != "" {
|
||||
g.mockNames = parseMockNames(*mockNames)
|
||||
}
|
||||
if *copyrightFile != "" {
|
||||
header, err := os.ReadFile(*copyrightFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed reading copyright file: %v", err)
|
||||
}
|
||||
|
||||
g.copyrightHeader = string(header)
|
||||
}
|
||||
if err := g.Generate(pkg, outputPackageName, outputPackagePath); err != nil {
|
||||
log.Fatalf("Failed generating mock: %v", err)
|
||||
}
|
||||
output := g.Output()
|
||||
dst := os.Stdout
|
||||
if len(*destination) > 0 {
|
||||
if err := os.MkdirAll(filepath.Dir(*destination), os.ModePerm); err != nil {
|
||||
log.Fatalf("Unable to create directory: %v", err)
|
||||
}
|
||||
existing, err := os.ReadFile(*destination)
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
log.Fatalf("Failed reading pre-exiting destination file: %v", err)
|
||||
}
|
||||
if len(existing) == len(output) && bytes.Equal(existing, output) {
|
||||
return
|
||||
}
|
||||
f, err := os.Create(*destination)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed opening destination file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
dst = f
|
||||
}
|
||||
if _, err := dst.Write(output); err != nil {
|
||||
log.Fatalf("Failed writing to destination: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func parseMockNames(names string) map[string]string {
|
||||
mocksMap := make(map[string]string)
|
||||
for _, kv := range strings.Split(names, ",") {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if len(parts) != 2 || parts[1] == "" {
|
||||
log.Fatalf("bad mock names spec: %v", kv)
|
||||
}
|
||||
mocksMap[parts[0]] = parts[1]
|
||||
}
|
||||
return mocksMap
|
||||
}
|
||||
|
||||
func parseExcludeInterfaces(names string) map[string]struct{} {
|
||||
splitNames := strings.Split(names, ",")
|
||||
namesSet := make(map[string]struct{}, len(splitNames))
|
||||
for _, name := range splitNames {
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
namesSet[name] = struct{}{}
|
||||
}
|
||||
|
||||
if len(namesSet) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return namesSet
|
||||
}
|
||||
|
||||
func usage() {
|
||||
_, _ = io.WriteString(os.Stderr, usageText)
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
const usageText = `mockgen has two modes of operation: source and reflect.
|
||||
|
||||
Source mode generates mock interfaces from a source file.
|
||||
It is enabled by using the -source flag. Other flags that
|
||||
may be useful in this mode are -imports and -aux_files.
|
||||
Example:
|
||||
mockgen -source=foo.go [other options]
|
||||
|
||||
Reflect mode generates mock interfaces by building a program
|
||||
that uses reflection to understand interfaces. It is enabled
|
||||
by passing two non-flag arguments: an import path, and a
|
||||
comma-separated list of symbols.
|
||||
Example:
|
||||
mockgen database/sql/driver Conn,Driver
|
||||
|
||||
`
|
||||
|
||||
type generator struct {
|
||||
buf bytes.Buffer
|
||||
indent string
|
||||
mockNames map[string]string // may be empty
|
||||
filename string // may be empty
|
||||
destination string // may be empty
|
||||
srcPackage, srcInterfaces string // may be empty
|
||||
copyrightHeader string
|
||||
|
||||
packageMap map[string]string // map from import path to package name
|
||||
}
|
||||
|
||||
func (g *generator) p(format string, args ...any) {
|
||||
fmt.Fprintf(&g.buf, g.indent+format+"\n", args...)
|
||||
}
|
||||
|
||||
func (g *generator) in() {
|
||||
g.indent += "\t"
|
||||
}
|
||||
|
||||
func (g *generator) out() {
|
||||
if len(g.indent) > 0 {
|
||||
g.indent = g.indent[0 : len(g.indent)-1]
|
||||
}
|
||||
}
|
||||
|
||||
// sanitize cleans up a string to make a suitable package name.
|
||||
func sanitize(s string) string {
|
||||
t := ""
|
||||
for _, r := range s {
|
||||
if t == "" {
|
||||
if unicode.IsLetter(r) || r == '_' {
|
||||
t += string(r)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
|
||||
t += string(r)
|
||||
continue
|
||||
}
|
||||
}
|
||||
t += "_"
|
||||
}
|
||||
if t == "_" {
|
||||
t = "x"
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (g *generator) Generate(pkg *model.Package, outputPkgName string, outputPackagePath string) error {
|
||||
if outputPkgName != pkg.Name && *selfPackage == "" {
|
||||
// reset outputPackagePath if it's not passed in through -self_package
|
||||
outputPackagePath = ""
|
||||
}
|
||||
|
||||
if g.copyrightHeader != "" {
|
||||
lines := strings.Split(g.copyrightHeader, "\n")
|
||||
for _, line := range lines {
|
||||
g.p("// %s", line)
|
||||
}
|
||||
g.p("")
|
||||
}
|
||||
|
||||
g.p("// Code generated by MockGen. DO NOT EDIT.")
|
||||
if *writeSourceComment {
|
||||
if g.filename != "" {
|
||||
g.p("// Source: %v", g.filename)
|
||||
} else {
|
||||
g.p("// Source: %v (interfaces: %v)", g.srcPackage, g.srcInterfaces)
|
||||
}
|
||||
}
|
||||
g.p("//")
|
||||
g.p("// Generated by this command:")
|
||||
// only log the name of the executable, not the full path
|
||||
g.p("// %v", strings.Join(append([]string{filepath.Base(os.Args[0])}, os.Args[1:]...), " "))
|
||||
|
||||
// Get all required imports, and generate unique names for them all.
|
||||
im := pkg.Imports()
|
||||
im[gomockImportPath] = true
|
||||
|
||||
// Only import reflect if it's used. We only use reflect in mocked methods
|
||||
// so only import if any of the mocked interfaces have methods.
|
||||
for _, intf := range pkg.Interfaces {
|
||||
if len(intf.Methods) > 0 {
|
||||
im["reflect"] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Sort keys to make import alias generation predictable
|
||||
sortedPaths := make([]string, len(im))
|
||||
x := 0
|
||||
for pth := range im {
|
||||
sortedPaths[x] = pth
|
||||
x++
|
||||
}
|
||||
sort.Strings(sortedPaths)
|
||||
|
||||
packagesName := createPackageMap(sortedPaths)
|
||||
|
||||
definedImports := make(map[string]string, len(im))
|
||||
if *imports != "" {
|
||||
for _, kv := range strings.Split(*imports, ",") {
|
||||
eq := strings.Index(kv, "=")
|
||||
if k, v := kv[:eq], kv[eq+1:]; k != "." {
|
||||
definedImports[v] = k
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.packageMap = make(map[string]string, len(im))
|
||||
localNames := make(map[string]bool, len(im))
|
||||
for _, pth := range sortedPaths {
|
||||
base, ok := packagesName[pth]
|
||||
if !ok {
|
||||
base = sanitize(path.Base(pth))
|
||||
}
|
||||
|
||||
// Local names for an imported package can usually be the basename of the import path.
|
||||
// A couple of situations don't permit that, such as duplicate local names
|
||||
// (e.g. importing "html/template" and "text/template"), or where the basename is
|
||||
// a keyword (e.g. "foo/case") or when defining a name for that by using the -imports flag.
|
||||
// try base0, base1, ...
|
||||
pkgName := base
|
||||
|
||||
if _, ok := definedImports[base]; ok {
|
||||
pkgName = definedImports[base]
|
||||
}
|
||||
|
||||
i := 0
|
||||
for localNames[pkgName] || token.Lookup(pkgName).IsKeyword() {
|
||||
pkgName = base + strconv.Itoa(i)
|
||||
i++
|
||||
}
|
||||
|
||||
// Avoid importing package if source pkg == output pkg
|
||||
if pth == pkg.PkgPath && outputPackagePath == pkg.PkgPath {
|
||||
continue
|
||||
}
|
||||
|
||||
g.packageMap[pth] = pkgName
|
||||
localNames[pkgName] = true
|
||||
}
|
||||
|
||||
if *writePkgComment {
|
||||
g.p("// Package %v is a generated GoMock package.", outputPkgName)
|
||||
}
|
||||
g.p("package %v", outputPkgName)
|
||||
g.p("")
|
||||
g.p("import (")
|
||||
g.in()
|
||||
for pkgPath, pkgName := range g.packageMap {
|
||||
if pkgPath == outputPackagePath {
|
||||
continue
|
||||
}
|
||||
g.p("%v %q", pkgName, pkgPath)
|
||||
}
|
||||
for _, pkgPath := range pkg.DotImports {
|
||||
g.p(". %q", pkgPath)
|
||||
}
|
||||
g.out()
|
||||
g.p(")")
|
||||
|
||||
if *writeGenerateDirective {
|
||||
g.p("//go:generate %v", strings.Join(os.Args, " "))
|
||||
}
|
||||
|
||||
for _, intf := range pkg.Interfaces {
|
||||
if err := g.GenerateMockInterface(intf, outputPackagePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// The name of the mock type to use for the given interface identifier.
|
||||
func (g *generator) mockName(typeName string) string {
|
||||
if mockName, ok := g.mockNames[typeName]; ok {
|
||||
return mockName
|
||||
}
|
||||
|
||||
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%v struct {", mockType, longTp)
|
||||
g.in()
|
||||
g.p("ctrl *gomock.Controller")
|
||||
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%v struct {", mockType, longTp)
|
||||
g.in()
|
||||
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%v(ctrl *gomock.Controller) *%v%v {", mockType, longTp, mockType, shortTp)
|
||||
g.in()
|
||||
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("}")
|
||||
g.p("")
|
||||
|
||||
// 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%v) EXPECT() *%vMockRecorder%v {", mockType, shortTp, mockType, shortTp)
|
||||
g.in()
|
||||
g.p("return m.recorder")
|
||||
g.out()
|
||||
g.p("}")
|
||||
|
||||
g.GenerateMockMethods(mockType, intf, outputPackagePath, longTp, shortTp, *typed)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type byMethodName []*model.Method
|
||||
|
||||
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, longTp, shortTp string, typed bool) {
|
||||
sort.Sort(byMethodName(intf.Methods))
|
||||
for _, m := range intf.Methods {
|
||||
g.p("")
|
||||
_ = g.GenerateMockMethod(mockType, m, pkgOverride, shortTp)
|
||||
g.p("")
|
||||
_ = g.GenerateMockRecorderMethod(intf, mockType, m, shortTp, typed)
|
||||
if typed {
|
||||
g.p("")
|
||||
_ = g.GenerateMockReturnCallMethod(intf, m, pkgOverride, longTp, shortTp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeArgString(argNames, argTypes []string) string {
|
||||
args := make([]string, len(argNames))
|
||||
for i, name := range argNames {
|
||||
// specify the type only once for consecutive args of the same type
|
||||
if i+1 < len(argTypes) && argTypes[i] == argTypes[i+1] {
|
||||
args[i] = name
|
||||
} else {
|
||||
args[i] = name + " " + argTypes[i]
|
||||
}
|
||||
}
|
||||
return strings.Join(args, ", ")
|
||||
}
|
||||
|
||||
// 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, shortTp string) error {
|
||||
argNames := g.getArgNames(m, true /* in */)
|
||||
argTypes := g.getArgTypes(m, pkgOverride, true /* in */)
|
||||
argString := makeArgString(argNames, argTypes)
|
||||
|
||||
rets := make([]string, len(m.Out))
|
||||
for i, p := range m.Out {
|
||||
rets[i] = p.Type.String(g.packageMap, pkgOverride)
|
||||
}
|
||||
retString := strings.Join(rets, ", ")
|
||||
if len(rets) > 1 {
|
||||
retString = "(" + retString + ")"
|
||||
}
|
||||
if retString != "" {
|
||||
retString = " " + retString
|
||||
}
|
||||
|
||||
ia := newIdentifierAllocator(argNames)
|
||||
idRecv := ia.allocateIdentifier("m")
|
||||
|
||||
g.p("// %v mocks base method.", m.Name)
|
||||
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)
|
||||
|
||||
var callArgs string
|
||||
if m.Variadic == nil {
|
||||
if len(argNames) > 0 {
|
||||
callArgs = ", " + strings.Join(argNames, ", ")
|
||||
}
|
||||
} else {
|
||||
// Non-trivial. The generated code must build a []any,
|
||||
// but the variadic argument may be any type.
|
||||
idVarArgs := ia.allocateIdentifier("varargs")
|
||||
idVArg := ia.allocateIdentifier("a")
|
||||
g.p("%s := []any{%s}", idVarArgs, strings.Join(argNames[:len(argNames)-1], ", "))
|
||||
g.p("for _, %s := range %s {", idVArg, argNames[len(argNames)-1])
|
||||
g.in()
|
||||
g.p("%s = append(%s, %s)", idVarArgs, idVarArgs, idVArg)
|
||||
g.out()
|
||||
g.p("}")
|
||||
callArgs = ", " + idVarArgs + "..."
|
||||
}
|
||||
if len(m.Out) == 0 {
|
||||
g.p(`%v.ctrl.Call(%v, %q%v)`, idRecv, idRecv, m.Name, callArgs)
|
||||
} else {
|
||||
idRet := ia.allocateIdentifier("ret")
|
||||
g.p(`%v := %v.ctrl.Call(%v, %q%v)`, idRet, idRecv, idRecv, m.Name, callArgs)
|
||||
|
||||
// Go does not allow "naked" type assertions on nil values, so we use the two-value form here.
|
||||
// The value of that is either (x.(T), true) or (Z, false), where Z is the zero value for T.
|
||||
// Happily, this coincides with the semantics we want here.
|
||||
retNames := make([]string, len(rets))
|
||||
for i, t := range rets {
|
||||
retNames[i] = ia.allocateIdentifier(fmt.Sprintf("ret%d", i))
|
||||
g.p("%s, _ := %s[%d].(%s)", retNames[i], idRet, i, t)
|
||||
}
|
||||
g.p("return " + strings.Join(retNames, ", "))
|
||||
}
|
||||
|
||||
g.out()
|
||||
g.p("}")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *generator) GenerateMockRecorderMethod(intf *model.Interface, mockType string, m *model.Method, shortTp string, typed bool) error {
|
||||
argNames := g.getArgNames(m, true)
|
||||
|
||||
var argString string
|
||||
if m.Variadic == nil {
|
||||
argString = strings.Join(argNames, ", ")
|
||||
} else {
|
||||
argString = strings.Join(argNames[:len(argNames)-1], ", ")
|
||||
}
|
||||
if argString != "" {
|
||||
argString += " any"
|
||||
}
|
||||
|
||||
if m.Variadic != nil {
|
||||
if argString != "" {
|
||||
argString += ", "
|
||||
}
|
||||
argString += fmt.Sprintf("%s ...any", argNames[len(argNames)-1])
|
||||
}
|
||||
|
||||
ia := newIdentifierAllocator(argNames)
|
||||
idRecv := ia.allocateIdentifier("mr")
|
||||
|
||||
g.p("// %v indicates an expected call of %v.", m.Name, m.Name)
|
||||
if typed {
|
||||
g.p("func (%s *%vMockRecorder%v) %v(%v) *%s%sCall%s {", idRecv, mockType, shortTp, m.Name, argString, intf.Name, m.Name, shortTp)
|
||||
} else {
|
||||
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)
|
||||
|
||||
var callArgs string
|
||||
if m.Variadic == nil {
|
||||
if len(argNames) > 0 {
|
||||
callArgs = ", " + strings.Join(argNames, ", ")
|
||||
}
|
||||
} else {
|
||||
if len(argNames) == 1 {
|
||||
// Easy: just use ... to push the arguments through.
|
||||
callArgs = ", " + argNames[0] + "..."
|
||||
} else {
|
||||
// Hard: create a temporary slice.
|
||||
idVarArgs := ia.allocateIdentifier("varargs")
|
||||
g.p("%s := append([]any{%s}, %s...)",
|
||||
idVarArgs,
|
||||
strings.Join(argNames[:len(argNames)-1], ", "),
|
||||
argNames[len(argNames)-1])
|
||||
callArgs = ", " + idVarArgs + "..."
|
||||
}
|
||||
}
|
||||
if typed {
|
||||
g.p(`call := %s.mock.ctrl.RecordCallWithMethodType(%s.mock, "%s", reflect.TypeOf((*%s%s)(nil).%s)%s)`, idRecv, idRecv, m.Name, mockType, shortTp, m.Name, callArgs)
|
||||
g.p(`return &%s%sCall%s{Call: call}`, intf.Name, m.Name, shortTp)
|
||||
} else {
|
||||
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("}")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *generator) GenerateMockReturnCallMethod(intf *model.Interface, m *model.Method, pkgOverride, longTp, shortTp string) error {
|
||||
argNames := g.getArgNames(m, true /* in */)
|
||||
retNames := g.getArgNames(m, false /* out */)
|
||||
argTypes := g.getArgTypes(m, pkgOverride, true /* in */)
|
||||
retTypes := g.getArgTypes(m, pkgOverride, false /* out */)
|
||||
argString := strings.Join(argTypes, ", ")
|
||||
|
||||
rets := make([]string, len(m.Out))
|
||||
for i, p := range m.Out {
|
||||
rets[i] = p.Type.String(g.packageMap, pkgOverride)
|
||||
}
|
||||
|
||||
var retString string
|
||||
switch {
|
||||
case len(rets) == 1:
|
||||
retString = " " + rets[0]
|
||||
case len(rets) > 1:
|
||||
retString = " (" + strings.Join(rets, ", ") + ")"
|
||||
}
|
||||
|
||||
ia := newIdentifierAllocator(argNames)
|
||||
idRecv := ia.allocateIdentifier("c")
|
||||
|
||||
recvStructName := intf.Name + m.Name
|
||||
|
||||
g.p("// %s%sCall wrap *gomock.Call", intf.Name, m.Name)
|
||||
g.p("type %s%sCall%s struct{", intf.Name, m.Name, longTp)
|
||||
g.in()
|
||||
g.p("*gomock.Call")
|
||||
g.out()
|
||||
g.p("}")
|
||||
|
||||
g.p("// Return rewrite *gomock.Call.Return")
|
||||
g.p("func (%s *%sCall%s) Return(%v) *%sCall%s {", idRecv, recvStructName, shortTp, makeArgString(retNames, retTypes), recvStructName, shortTp)
|
||||
g.in()
|
||||
var retArgs string
|
||||
if len(retNames) > 0 {
|
||||
retArgs = strings.Join(retNames, ", ")
|
||||
}
|
||||
g.p(`%s.Call = %v.Call.Return(%v)`, idRecv, idRecv, retArgs)
|
||||
g.p("return %s", idRecv)
|
||||
g.out()
|
||||
g.p("}")
|
||||
|
||||
g.p("// Do rewrite *gomock.Call.Do")
|
||||
g.p("func (%s *%sCall%s) Do(f func(%v)%v) *%sCall%s {", idRecv, recvStructName, shortTp, argString, retString, recvStructName, shortTp)
|
||||
g.in()
|
||||
g.p(`%s.Call = %v.Call.Do(f)`, idRecv, idRecv)
|
||||
g.p("return %s", idRecv)
|
||||
g.out()
|
||||
g.p("}")
|
||||
|
||||
g.p("// DoAndReturn rewrite *gomock.Call.DoAndReturn")
|
||||
g.p("func (%s *%sCall%s) DoAndReturn(f func(%v)%v) *%sCall%s {", idRecv, recvStructName, shortTp, argString, retString, recvStructName, shortTp)
|
||||
g.in()
|
||||
g.p(`%s.Call = %v.Call.DoAndReturn(f)`, idRecv, idRecv)
|
||||
g.p("return %s", idRecv)
|
||||
g.out()
|
||||
g.p("}")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *generator) getArgNames(m *model.Method, in bool) []string {
|
||||
var params []*model.Parameter
|
||||
if in {
|
||||
params = m.In
|
||||
} else {
|
||||
params = m.Out
|
||||
}
|
||||
argNames := make([]string, len(params))
|
||||
for i, p := range params {
|
||||
name := p.Name
|
||||
if name == "" || name == "_" {
|
||||
name = fmt.Sprintf("arg%d", i)
|
||||
}
|
||||
argNames[i] = name
|
||||
}
|
||||
if m.Variadic != nil && in {
|
||||
name := m.Variadic.Name
|
||||
if name == "" {
|
||||
name = fmt.Sprintf("arg%d", len(params))
|
||||
}
|
||||
argNames = append(argNames, name)
|
||||
}
|
||||
return argNames
|
||||
}
|
||||
|
||||
func (g *generator) getArgTypes(m *model.Method, pkgOverride string, in bool) []string {
|
||||
var params []*model.Parameter
|
||||
if in {
|
||||
params = m.In
|
||||
} else {
|
||||
params = m.Out
|
||||
}
|
||||
argTypes := make([]string, len(params))
|
||||
for i, p := range params {
|
||||
argTypes[i] = p.Type.String(g.packageMap, pkgOverride)
|
||||
}
|
||||
if m.Variadic != nil {
|
||||
argTypes = append(argTypes, "..."+m.Variadic.Type.String(g.packageMap, pkgOverride))
|
||||
}
|
||||
return argTypes
|
||||
}
|
||||
|
||||
type identifierAllocator map[string]struct{}
|
||||
|
||||
func newIdentifierAllocator(taken []string) identifierAllocator {
|
||||
a := make(identifierAllocator, len(taken))
|
||||
for _, s := range taken {
|
||||
a[s] = struct{}{}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (o identifierAllocator) allocateIdentifier(want string) string {
|
||||
id := want
|
||||
for i := 2; ; i++ {
|
||||
if _, ok := o[id]; !ok {
|
||||
o[id] = struct{}{}
|
||||
return id
|
||||
}
|
||||
id = want + "_" + strconv.Itoa(i)
|
||||
}
|
||||
}
|
||||
|
||||
// Output returns the generator's output, formatted in the standard Go style.
|
||||
func (g *generator) Output() []byte {
|
||||
src, err := toolsimports.Process(g.destination, g.buf.Bytes(), nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to format generated source code: %s\n%s", err, g.buf.String())
|
||||
}
|
||||
return src
|
||||
}
|
||||
|
||||
// createPackageMap returns a map of import path to package name
|
||||
// for specified importPaths.
|
||||
func createPackageMap(importPaths []string) map[string]string {
|
||||
var pkg struct {
|
||||
Name string
|
||||
ImportPath string
|
||||
}
|
||||
pkgMap := make(map[string]string)
|
||||
b := bytes.NewBuffer(nil)
|
||||
args := []string{"list", "-json"}
|
||||
args = append(args, importPaths...)
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Stdout = b
|
||||
cmd.Run()
|
||||
dec := json.NewDecoder(b)
|
||||
for dec.More() {
|
||||
err := dec.Decode(&pkg)
|
||||
if err != nil {
|
||||
log.Printf("failed to decode 'go list' output: %v", err)
|
||||
continue
|
||||
}
|
||||
pkgMap[pkg.ImportPath] = pkg.Name
|
||||
}
|
||||
return pkgMap
|
||||
}
|
||||
|
||||
func printVersion() {
|
||||
if version != "" {
|
||||
fmt.Printf("v%s\nCommit: %s\nDate: %s\n", version, commit, date)
|
||||
} else {
|
||||
printModuleVersion()
|
||||
}
|
||||
}
|
||||
|
||||
// parseImportPackage get package import path via source file
|
||||
// an alternative implementation is to use:
|
||||
// cfg := &packages.Config{Mode: packages.NeedName, Tests: true, Dir: srcDir}
|
||||
// pkgs, err := packages.Load(cfg, "file="+source)
|
||||
// However, it will call "go list" and slow down the performance
|
||||
func parsePackageImport(srcDir string) (string, error) {
|
||||
moduleMode := os.Getenv("GO111MODULE")
|
||||
// trying to find the module
|
||||
if moduleMode != "off" {
|
||||
currentDir := srcDir
|
||||
for {
|
||||
dat, err := os.ReadFile(filepath.Join(currentDir, "go.mod"))
|
||||
if os.IsNotExist(err) {
|
||||
if currentDir == filepath.Dir(currentDir) {
|
||||
// at the root
|
||||
break
|
||||
}
|
||||
currentDir = filepath.Dir(currentDir)
|
||||
continue
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
modulePath := modfile.ModulePath(dat)
|
||||
return filepath.ToSlash(filepath.Join(modulePath, strings.TrimPrefix(srcDir, currentDir))), nil
|
||||
}
|
||||
}
|
||||
// fall back to GOPATH mode
|
||||
goPaths := os.Getenv("GOPATH")
|
||||
if goPaths == "" {
|
||||
return "", fmt.Errorf("GOPATH is not set")
|
||||
}
|
||||
goPathList := strings.Split(goPaths, string(os.PathListSeparator))
|
||||
for _, goPath := range goPathList {
|
||||
sourceRoot := filepath.Join(goPath, "src") + string(os.PathSeparator)
|
||||
if strings.HasPrefix(srcDir, sourceRoot) {
|
||||
return filepath.ToSlash(strings.TrimPrefix(srcDir, sourceRoot)), nil
|
||||
}
|
||||
}
|
||||
return "", errOutsideGoPath
|
||||
}
|
533
vendor/go.uber.org/mock/mockgen/model/model.go
generated
vendored
Normal file
533
vendor/go.uber.org/mock/mockgen/model/model.go
generated
vendored
Normal file
@@ -0,0 +1,533 @@
|
||||
// Copyright 2012 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 model contains the data model necessary for generating mock implementations.
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// pkgPath is the importable path for package model
|
||||
const pkgPath = "go.uber.org/mock/mockgen/model"
|
||||
|
||||
// Package is a Go package. It may be a subset.
|
||||
type Package struct {
|
||||
Name string
|
||||
PkgPath string
|
||||
Interfaces []*Interface
|
||||
DotImports []string
|
||||
}
|
||||
|
||||
// Print writes the package name and its exported interfaces.
|
||||
func (pkg *Package) Print(w io.Writer) {
|
||||
_, _ = fmt.Fprintf(w, "package %s\n", pkg.Name)
|
||||
for _, intf := range pkg.Interfaces {
|
||||
intf.Print(w)
|
||||
}
|
||||
}
|
||||
|
||||
// Imports returns the imports needed by the Package as a set of import paths.
|
||||
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
|
||||
TypeParams []*Parameter
|
||||
}
|
||||
|
||||
// Print writes the interface name and its methods.
|
||||
func (intf *Interface) Print(w io.Writer) {
|
||||
_, _ = fmt.Fprintf(w, "interface %s\n", intf.Name)
|
||||
for _, m := range intf.Methods {
|
||||
m.Print(w)
|
||||
}
|
||||
}
|
||||
|
||||
func (intf *Interface) addImports(im map[string]bool) {
|
||||
for _, m := range intf.Methods {
|
||||
m.addImports(im)
|
||||
}
|
||||
}
|
||||
|
||||
// AddMethod adds a new method, de-duplicating by method name.
|
||||
func (intf *Interface) AddMethod(m *Method) {
|
||||
for _, me := range intf.Methods {
|
||||
if me.Name == m.Name {
|
||||
return
|
||||
}
|
||||
}
|
||||
intf.Methods = append(intf.Methods, m)
|
||||
}
|
||||
|
||||
// Method is a single method of an interface.
|
||||
type Method struct {
|
||||
Name string
|
||||
In, Out []*Parameter
|
||||
Variadic *Parameter // may be nil
|
||||
}
|
||||
|
||||
// Print writes the method name and its signature.
|
||||
func (m *Method) Print(w io.Writer) {
|
||||
_, _ = fmt.Fprintf(w, " - method %s\n", m.Name)
|
||||
if len(m.In) > 0 {
|
||||
_, _ = fmt.Fprintf(w, " in:\n")
|
||||
for _, p := range m.In {
|
||||
p.Print(w)
|
||||
}
|
||||
}
|
||||
if m.Variadic != nil {
|
||||
_, _ = fmt.Fprintf(w, " ...:\n")
|
||||
m.Variadic.Print(w)
|
||||
}
|
||||
if len(m.Out) > 0 {
|
||||
_, _ = fmt.Fprintf(w, " out:\n")
|
||||
for _, p := range m.Out {
|
||||
p.Print(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Method) addImports(im map[string]bool) {
|
||||
for _, p := range m.In {
|
||||
p.Type.addImports(im)
|
||||
}
|
||||
if m.Variadic != nil {
|
||||
m.Variadic.Type.addImports(im)
|
||||
}
|
||||
for _, p := range m.Out {
|
||||
p.Type.addImports(im)
|
||||
}
|
||||
}
|
||||
|
||||
// Parameter is an argument or return parameter of a method.
|
||||
type Parameter struct {
|
||||
Name string // may be empty
|
||||
Type Type
|
||||
}
|
||||
|
||||
// Print writes a method parameter.
|
||||
func (p *Parameter) Print(w io.Writer) {
|
||||
n := p.Name
|
||||
if n == "" {
|
||||
n = `""`
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, " - %v: %v\n", n, p.Type.String(nil, ""))
|
||||
}
|
||||
|
||||
// Type is a Go type.
|
||||
type Type interface {
|
||||
String(pm map[string]string, pkgOverride string) string
|
||||
addImports(im map[string]bool)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Call gob.RegisterName with pkgPath as prefix to avoid conflicting with
|
||||
// github.com/golang/mock/mockgen/model 's registration.
|
||||
gob.RegisterName(pkgPath+".ArrayType", &ArrayType{})
|
||||
gob.RegisterName(pkgPath+".ChanType", &ChanType{})
|
||||
gob.RegisterName(pkgPath+".FuncType", &FuncType{})
|
||||
gob.RegisterName(pkgPath+".MapType", &MapType{})
|
||||
gob.RegisterName(pkgPath+".NamedType", &NamedType{})
|
||||
gob.RegisterName(pkgPath+".PointerType", &PointerType{})
|
||||
|
||||
// Call gob.RegisterName to make sure it has the consistent name registered
|
||||
// for both gob decoder and encoder.
|
||||
//
|
||||
// For a non-pointer type, gob.Register will try to get package full path by
|
||||
// calling rt.PkgPath() for a name to register. If your project has vendor
|
||||
// directory, it is possible that PkgPath will get a path like this:
|
||||
// ../../../vendor/go.uber.org/mock/mockgen/model
|
||||
gob.RegisterName(pkgPath+".PredeclaredType", PredeclaredType(""))
|
||||
}
|
||||
|
||||
// ArrayType is an array or slice type.
|
||||
type ArrayType struct {
|
||||
Len int // -1 for slices, >= 0 for arrays
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (at *ArrayType) String(pm map[string]string, pkgOverride string) string {
|
||||
s := "[]"
|
||||
if at.Len > -1 {
|
||||
s = fmt.Sprintf("[%d]", at.Len)
|
||||
}
|
||||
return s + at.Type.String(pm, pkgOverride)
|
||||
}
|
||||
|
||||
func (at *ArrayType) addImports(im map[string]bool) { at.Type.addImports(im) }
|
||||
|
||||
// ChanType is a channel type.
|
||||
type ChanType struct {
|
||||
Dir ChanDir // 0, 1 or 2
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (ct *ChanType) String(pm map[string]string, pkgOverride string) string {
|
||||
s := ct.Type.String(pm, pkgOverride)
|
||||
if ct.Dir == RecvDir {
|
||||
return "<-chan " + s
|
||||
}
|
||||
if ct.Dir == SendDir {
|
||||
return "chan<- " + s
|
||||
}
|
||||
return "chan " + s
|
||||
}
|
||||
|
||||
func (ct *ChanType) addImports(im map[string]bool) { ct.Type.addImports(im) }
|
||||
|
||||
// ChanDir is a channel direction.
|
||||
type ChanDir int
|
||||
|
||||
// Constants for channel directions.
|
||||
const (
|
||||
RecvDir ChanDir = 1
|
||||
SendDir ChanDir = 2
|
||||
)
|
||||
|
||||
// FuncType is a function type.
|
||||
type FuncType struct {
|
||||
In, Out []*Parameter
|
||||
Variadic *Parameter // may be nil
|
||||
}
|
||||
|
||||
func (ft *FuncType) String(pm map[string]string, pkgOverride string) string {
|
||||
args := make([]string, len(ft.In))
|
||||
for i, p := range ft.In {
|
||||
args[i] = p.Type.String(pm, pkgOverride)
|
||||
}
|
||||
if ft.Variadic != nil {
|
||||
args = append(args, "..."+ft.Variadic.Type.String(pm, pkgOverride))
|
||||
}
|
||||
rets := make([]string, len(ft.Out))
|
||||
for i, p := range ft.Out {
|
||||
rets[i] = p.Type.String(pm, pkgOverride)
|
||||
}
|
||||
retString := strings.Join(rets, ", ")
|
||||
if nOut := len(ft.Out); nOut == 1 {
|
||||
retString = " " + retString
|
||||
} else if nOut > 1 {
|
||||
retString = " (" + retString + ")"
|
||||
}
|
||||
return "func(" + strings.Join(args, ", ") + ")" + retString
|
||||
}
|
||||
|
||||
func (ft *FuncType) addImports(im map[string]bool) {
|
||||
for _, p := range ft.In {
|
||||
p.Type.addImports(im)
|
||||
}
|
||||
if ft.Variadic != nil {
|
||||
ft.Variadic.Type.addImports(im)
|
||||
}
|
||||
for _, p := range ft.Out {
|
||||
p.Type.addImports(im)
|
||||
}
|
||||
}
|
||||
|
||||
// MapType is a map type.
|
||||
type MapType struct {
|
||||
Key, Value Type
|
||||
}
|
||||
|
||||
func (mt *MapType) String(pm map[string]string, pkgOverride string) string {
|
||||
return "map[" + mt.Key.String(pm, pkgOverride) + "]" + mt.Value.String(pm, pkgOverride)
|
||||
}
|
||||
|
||||
func (mt *MapType) addImports(im map[string]bool) {
|
||||
mt.Key.addImports(im)
|
||||
mt.Value.addImports(im)
|
||||
}
|
||||
|
||||
// NamedType is an exported type in a package.
|
||||
type NamedType struct {
|
||||
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 + nt.TypeParams.String(pm, pkgOverride)
|
||||
}
|
||||
prefix := pm[nt.Package]
|
||||
if prefix != "" {
|
||||
return prefix + "." + nt.Type + nt.TypeParams.String(pm, pkgOverride)
|
||||
}
|
||||
|
||||
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.
|
||||
type PointerType struct {
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (pt *PointerType) String(pm map[string]string, pkgOverride string) string {
|
||||
return "*" + pt.Type.String(pm, pkgOverride)
|
||||
}
|
||||
func (pt *PointerType) addImports(im map[string]bool) { pt.Type.addImports(im) }
|
||||
|
||||
// PredeclaredType is a predeclared type such as "int".
|
||||
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
|
||||
// given reflection interface type.
|
||||
func InterfaceFromInterfaceType(it reflect.Type) (*Interface, error) {
|
||||
if it.Kind() != reflect.Interface {
|
||||
return nil, fmt.Errorf("%v is not an interface", it)
|
||||
}
|
||||
intf := &Interface{}
|
||||
|
||||
for i := 0; i < it.NumMethod(); i++ {
|
||||
mt := it.Method(i)
|
||||
// TODO: need to skip unexported methods? or just raise an error?
|
||||
m := &Method{
|
||||
Name: mt.Name,
|
||||
}
|
||||
|
||||
var err error
|
||||
m.In, m.Variadic, m.Out, err = funcArgsFromType(mt.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
intf.AddMethod(m)
|
||||
}
|
||||
|
||||
return intf, nil
|
||||
}
|
||||
|
||||
// t's Kind must be a reflect.Func.
|
||||
func funcArgsFromType(t reflect.Type) (in []*Parameter, variadic *Parameter, out []*Parameter, err error) {
|
||||
nin := t.NumIn()
|
||||
if t.IsVariadic() {
|
||||
nin--
|
||||
}
|
||||
var p *Parameter
|
||||
for i := 0; i < nin; i++ {
|
||||
p, err = parameterFromType(t.In(i))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
in = append(in, p)
|
||||
}
|
||||
if t.IsVariadic() {
|
||||
p, err = parameterFromType(t.In(nin).Elem())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
variadic = p
|
||||
}
|
||||
for i := 0; i < t.NumOut(); i++ {
|
||||
p, err = parameterFromType(t.Out(i))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out = append(out, p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parameterFromType(t reflect.Type) (*Parameter, error) {
|
||||
tt, err := typeFromType(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Parameter{Type: tt}, nil
|
||||
}
|
||||
|
||||
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
var byteType = reflect.TypeOf(byte(0))
|
||||
|
||||
func typeFromType(t reflect.Type) (Type, error) {
|
||||
// Hack workaround for https://golang.org/issue/3853.
|
||||
// This explicit check should not be necessary.
|
||||
if t == byteType {
|
||||
return PredeclaredType("byte"), nil
|
||||
}
|
||||
|
||||
if imp := t.PkgPath(); imp != "" {
|
||||
return &NamedType{
|
||||
Package: impPath(imp),
|
||||
Type: t.Name(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// only unnamed or predeclared types after here
|
||||
|
||||
// Lots of types have element types. Let's do the parsing and error checking for all of them.
|
||||
var elemType Type
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
var err error
|
||||
elemType, err = typeFromType(t.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Array:
|
||||
return &ArrayType{
|
||||
Len: t.Len(),
|
||||
Type: elemType,
|
||||
}, nil
|
||||
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
|
||||
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String:
|
||||
return PredeclaredType(t.Kind().String()), nil
|
||||
case reflect.Chan:
|
||||
var dir ChanDir
|
||||
switch t.ChanDir() {
|
||||
case reflect.RecvDir:
|
||||
dir = RecvDir
|
||||
case reflect.SendDir:
|
||||
dir = SendDir
|
||||
}
|
||||
return &ChanType{
|
||||
Dir: dir,
|
||||
Type: elemType,
|
||||
}, nil
|
||||
case reflect.Func:
|
||||
in, variadic, out, err := funcArgsFromType(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FuncType{
|
||||
In: in,
|
||||
Out: out,
|
||||
Variadic: variadic,
|
||||
}, nil
|
||||
case reflect.Interface:
|
||||
// Two special interfaces.
|
||||
if t.NumMethod() == 0 {
|
||||
return PredeclaredType("any"), nil
|
||||
}
|
||||
if t == errorType {
|
||||
return PredeclaredType("error"), nil
|
||||
}
|
||||
case reflect.Map:
|
||||
kt, err := typeFromType(t.Key())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &MapType{
|
||||
Key: kt,
|
||||
Value: elemType,
|
||||
}, nil
|
||||
case reflect.Ptr:
|
||||
return &PointerType{
|
||||
Type: elemType,
|
||||
}, nil
|
||||
case reflect.Slice:
|
||||
return &ArrayType{
|
||||
Len: -1,
|
||||
Type: elemType,
|
||||
}, nil
|
||||
case reflect.Struct:
|
||||
if t.NumField() == 0 {
|
||||
return PredeclaredType("struct{}"), nil
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Struct, UnsafePointer
|
||||
return nil, fmt.Errorf("can't yet turn %v (%v) into a model.Type", t, t.Kind())
|
||||
}
|
||||
|
||||
// impPath sanitizes the package path returned by `PkgPath` method of a reflect Type so that
|
||||
// it is importable. PkgPath might return a path that includes "vendor". These paths do not
|
||||
// compile, so we need to remove everything up to and including "/vendor/".
|
||||
// See https://github.com/golang/go/issues/12019.
|
||||
func impPath(imp string) string {
|
||||
if strings.HasPrefix(imp, "vendor/") {
|
||||
imp = "/" + imp
|
||||
}
|
||||
if i := strings.LastIndex(imp, "/vendor/"); i != -1 {
|
||||
imp = imp[i+len("/vendor/"):]
|
||||
}
|
||||
return imp
|
||||
}
|
||||
|
||||
// ErrorInterface represent built-in error interface.
|
||||
var ErrorInterface = Interface{
|
||||
Name: "error",
|
||||
Methods: []*Method{
|
||||
{
|
||||
Name: "Error",
|
||||
Out: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: PredeclaredType("string"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
802
vendor/go.uber.org/mock/mockgen/parse.go
generated
vendored
Normal file
802
vendor/go.uber.org/mock/mockgen/parse.go
generated
vendored
Normal file
@@ -0,0 +1,802 @@
|
||||
// Copyright 2012 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 main
|
||||
|
||||
// This file contains the model construction by parsing source files.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/importer"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/mock/mockgen/model"
|
||||
)
|
||||
|
||||
// sourceMode generates mocks via source file.
|
||||
func sourceMode(source string) (*model.Package, error) {
|
||||
srcDir, err := filepath.Abs(filepath.Dir(source))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed getting source directory: %v", err)
|
||||
}
|
||||
|
||||
packageImport, err := parsePackageImport(srcDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fs := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fs, source, nil, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed parsing source file %v: %v", source, err)
|
||||
}
|
||||
|
||||
p := &fileParser{
|
||||
fileSet: fs,
|
||||
imports: make(map[string]importedPackage),
|
||||
importedInterfaces: newInterfaceCache(),
|
||||
auxInterfaces: newInterfaceCache(),
|
||||
srcDir: srcDir,
|
||||
}
|
||||
|
||||
// Handle -imports.
|
||||
dotImports := make(map[string]bool)
|
||||
if *imports != "" {
|
||||
for _, kv := range strings.Split(*imports, ",") {
|
||||
eq := strings.Index(kv, "=")
|
||||
k, v := kv[:eq], kv[eq+1:]
|
||||
if k == "." {
|
||||
dotImports[v] = true
|
||||
} else {
|
||||
p.imports[k] = importedPkg{path: v}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *excludeInterfaces != "" {
|
||||
p.excludeNamesSet = parseExcludeInterfaces(*excludeInterfaces)
|
||||
}
|
||||
|
||||
// Handle -aux_files.
|
||||
if err := p.parseAuxFiles(*auxFiles); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.addAuxInterfacesFromFile(packageImport, file) // this file
|
||||
|
||||
pkg, err := p.parseFile(packageImport, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for pkgPath := range dotImports {
|
||||
pkg.DotImports = append(pkg.DotImports, pkgPath)
|
||||
}
|
||||
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 ambiguous 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 interfaceCache struct {
|
||||
m map[string]map[string]*namedInterface
|
||||
}
|
||||
|
||||
func newInterfaceCache() *interfaceCache {
|
||||
return &interfaceCache{
|
||||
m: make(map[string]map[string]*namedInterface),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
excludeNamesSet map[string]struct{}
|
||||
}
|
||||
|
||||
func (p *fileParser) errorf(pos token.Pos, format string, args ...any) error {
|
||||
ps := p.fileSet.Position(pos)
|
||||
format = "%s:%d:%d: " + format
|
||||
args = append([]any{ps.Filename, ps.Line, ps.Column}, args...)
|
||||
return fmt.Errorf(format, args...)
|
||||
}
|
||||
|
||||
func (p *fileParser) parseAuxFiles(auxFiles string) error {
|
||||
auxFiles = strings.TrimSpace(auxFiles)
|
||||
if auxFiles == "" {
|
||||
return nil
|
||||
}
|
||||
for _, kv := range strings.Split(auxFiles, ",") {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("bad aux file spec: %v", kv)
|
||||
}
|
||||
pkg, fpath := parts[0], parts[1]
|
||||
|
||||
file, err := parser.ParseFile(p.fileSet, fpath, nil, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.auxFiles = append(p.auxFiles, file)
|
||||
p.addAuxInterfacesFromFile(pkg, file)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fileParser) addAuxInterfacesFromFile(pkg string, file *ast.File) {
|
||||
for ni := range iterInterfaces(file) {
|
||||
p.auxInterfaces.Set(pkg, ni.name.Name, ni)
|
||||
}
|
||||
}
|
||||
|
||||
// parseFile loads all file imports and auxiliary files import into the
|
||||
// fileParser, parses all file interfaces and returns package model.
|
||||
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, pkgI := range allImports {
|
||||
if _, ok := p.imports[pkg]; !ok {
|
||||
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, pkgI := range auxImports {
|
||||
if _, ok := p.imports[pkg]; !ok {
|
||||
p.imports[pkg] = pkgI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var is []*model.Interface
|
||||
for ni := range iterInterfaces(file) {
|
||||
if _, ok := p.excludeNamesSet[ni.name.String()]; ok {
|
||||
continue
|
||||
}
|
||||
i, err := p.parseInterface(ni.name.String(), importPath, ni)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
is = append(is, i)
|
||||
}
|
||||
return &model.Package{
|
||||
Name: file.Name.String(),
|
||||
PkgPath: importPath,
|
||||
Interfaces: is,
|
||||
DotImports: dotImports,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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: newInterfaceCache(),
|
||||
auxInterfaces: newInterfaceCache(),
|
||||
srcDir: p.srcDir,
|
||||
}
|
||||
|
||||
var pkgs map[string]*ast.Package
|
||||
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)
|
||||
for ni := range iterInterfaces(file) {
|
||||
newP.importedInterfaces.Set(path, ni.name.Name, ni)
|
||||
}
|
||||
imports, _ := importsOfFile(file)
|
||||
for pkgName, pkgI := range imports {
|
||||
newP.imports[pkgName] = pkgI
|
||||
}
|
||||
}
|
||||
return newP, nil
|
||||
}
|
||||
|
||||
func (p *fileParser) constructInstParams(pkg string, params []*ast.Field, instParams []model.Type, embeddedInstParams []ast.Expr, tps map[string]model.Type) ([]model.Type, error) {
|
||||
pm := make(map[string]int)
|
||||
var i int
|
||||
for _, v := range params {
|
||||
for _, n := range v.Names {
|
||||
pm[n.Name] = i
|
||||
instParams = append(instParams, model.PredeclaredType(n.Name))
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
var runtimeInstParams []model.Type
|
||||
for _, instParam := range embeddedInstParams {
|
||||
switch t := instParam.(type) {
|
||||
case *ast.Ident:
|
||||
if idx, ok := pm[t.Name]; ok {
|
||||
runtimeInstParams = append(runtimeInstParams, instParams[idx])
|
||||
continue
|
||||
}
|
||||
}
|
||||
modelType, err := p.parseType(pkg, instParam, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtimeInstParams = append(runtimeInstParams, modelType)
|
||||
}
|
||||
|
||||
return runtimeInstParams, nil
|
||||
}
|
||||
|
||||
func (p *fileParser) constructTps(it *namedInterface) (tps map[string]model.Type) {
|
||||
tps = make(map[string]model.Type)
|
||||
n := 0
|
||||
for _, tp := range it.typeParams {
|
||||
for _, tm := range tp.Names {
|
||||
tps[tm.Name] = nil
|
||||
if len(it.instTypes) != 0 {
|
||||
tps[tm.Name] = it.instTypes[n]
|
||||
n++
|
||||
}
|
||||
}
|
||||
}
|
||||
return tps
|
||||
}
|
||||
|
||||
// parseInterface loads interface specified by pkg and name, parses it and returns
|
||||
// a new model with the parsed.
|
||||
func (p *fileParser) parseInterface(name, pkg string, it *namedInterface) (*model.Interface, error) {
|
||||
iface := &model.Interface{Name: name}
|
||||
tps := p.constructTps(it)
|
||||
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 _, field := range it.it.Methods.List {
|
||||
var methods []*model.Method
|
||||
if methods, err = p.parseMethod(field, it, iface, pkg, tps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range methods {
|
||||
iface.AddMethod(m)
|
||||
}
|
||||
}
|
||||
return iface, nil
|
||||
}
|
||||
|
||||
func (p *fileParser) parseMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) {
|
||||
// {} for git diff
|
||||
{
|
||||
switch v := field.Type.(type) {
|
||||
case *ast.FuncType:
|
||||
if nn := len(field.Names); nn != 1 {
|
||||
return nil, fmt.Errorf("expected one name for interface %v, got %d", iface.Name, nn)
|
||||
}
|
||||
m := &model.Method{
|
||||
Name: field.Names[0].String(),
|
||||
}
|
||||
var err error
|
||||
m.In, m.Variadic, m.Out, err = p.parseFunc(pkg, v, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*model.Method{m}, nil
|
||||
case *ast.Ident:
|
||||
// Embedded interface in this package.
|
||||
embeddedIfaceType := p.auxInterfaces.Get(pkg, v.String())
|
||||
if embeddedIfaceType == nil {
|
||||
embeddedIfaceType = p.importedInterfaces.Get(pkg, v.String())
|
||||
}
|
||||
|
||||
var embeddedIface *model.Interface
|
||||
if embeddedIfaceType != nil {
|
||||
var err error
|
||||
embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
embeddedIface, err = p.parseInterface(v.String(), pkg, embeddedIfaceType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else {
|
||||
// This is built-in error interface.
|
||||
if v.String() == model.ErrorInterface.Name {
|
||||
embeddedIface = &model.ErrorInterface
|
||||
} else {
|
||||
ip, err := p.parsePackage(pkg)
|
||||
if err != nil {
|
||||
return nil, p.errorf(v.Pos(), "could not parse package %s: %v", pkg, err)
|
||||
}
|
||||
|
||||
if embeddedIfaceType = ip.importedInterfaces.Get(pkg, v.String()); embeddedIfaceType == nil {
|
||||
return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", pkg, v.String())
|
||||
}
|
||||
|
||||
embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
embeddedIface, err = ip.parseInterface(v.String(), pkg, embeddedIfaceType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return embeddedIface.Methods, nil
|
||||
case *ast.SelectorExpr:
|
||||
// Embedded interface in another package.
|
||||
filePkg, sel := v.X.(*ast.Ident).String(), v.Sel.String()
|
||||
embeddedPkg, ok := p.imports[filePkg]
|
||||
if !ok {
|
||||
return nil, p.errorf(v.X.Pos(), "unknown package %s", filePkg)
|
||||
}
|
||||
|
||||
var embeddedIface *model.Interface
|
||||
var err error
|
||||
embeddedIfaceType := p.auxInterfaces.Get(filePkg, sel)
|
||||
if embeddedIfaceType != nil {
|
||||
embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
embeddedIface, err = p.parseInterface(sel, filePkg, embeddedIfaceType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
path := embeddedPkg.Path()
|
||||
parser := embeddedPkg.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[filePkg] = importedPkg{
|
||||
path: embeddedPkg.Path(),
|
||||
parser: parser,
|
||||
}
|
||||
}
|
||||
if embeddedIfaceType = parser.importedInterfaces.Get(path, sel); embeddedIfaceType == nil {
|
||||
return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", path, sel)
|
||||
}
|
||||
|
||||
embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
embeddedIface, err = parser.parseInterface(sel, path, embeddedIfaceType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// TODO: apply shadowing rules.
|
||||
return embeddedIface.Methods, nil
|
||||
default:
|
||||
return p.parseGenericMethod(field, it, iface, pkg, tps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *fileParser) parseFunc(pkg string, f *ast.FuncType, tps map[string]model.Type) (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, 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, 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, tps)
|
||||
if err != nil {
|
||||
return nil, nil, nil, p.errorf(f.Pos(), "failed parsing returns: %v", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field, tps map[string]model.Type) ([]*model.Parameter, error) {
|
||||
nf := 0
|
||||
for _, f := range fields {
|
||||
nn := len(f.Names)
|
||||
if nn == 0 {
|
||||
nn = 1 // anonymous parameter
|
||||
}
|
||||
nf += nn
|
||||
}
|
||||
if nf == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
ps := make([]*model.Parameter, nf)
|
||||
i := 0 // destination index
|
||||
for _, f := range fields {
|
||||
t, err := p.parseType(pkg, f.Type, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(f.Names) == 0 {
|
||||
// anonymous arg
|
||||
ps[i] = &model.Parameter{Type: t}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
for _, name := range f.Names {
|
||||
ps[i] = &model.Parameter{Name: name.Name, Type: t}
|
||||
i++
|
||||
}
|
||||
}
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func (p *fileParser) parseType(pkg string, typ ast.Expr, tps map[string]model.Type) (model.Type, error) {
|
||||
switch v := typ.(type) {
|
||||
case *ast.ArrayType:
|
||||
ln := -1
|
||||
if v.Len != nil {
|
||||
value, err := p.parseArrayLength(v.Len)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ln, err = strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return nil, p.errorf(v.Len.Pos(), "bad array size: %v", err)
|
||||
}
|
||||
}
|
||||
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, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var dir model.ChanDir
|
||||
if v.Dir == ast.SEND {
|
||||
dir = model.SendDir
|
||||
}
|
||||
if v.Dir == ast.RECV {
|
||||
dir = model.RecvDir
|
||||
}
|
||||
return &model.ChanType{Dir: dir, Type: t}, nil
|
||||
case *ast.Ellipsis:
|
||||
// assume we're parsing a variadic argument
|
||||
return p.parseType(pkg, v.Elt, tps)
|
||||
case *ast.FuncType:
|
||||
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:
|
||||
it, ok := tps[v.Name]
|
||||
if v.IsExported() && !ok {
|
||||
// `pkg` may be an aliased imported pkg
|
||||
// if so, patch the import w/ the fully qualified import
|
||||
maybeImportedPkg, ok := p.imports[pkg]
|
||||
if ok {
|
||||
pkg = maybeImportedPkg.Path()
|
||||
}
|
||||
// assume type in this package
|
||||
return &model.NamedType{Package: pkg, Type: v.Name}, nil
|
||||
}
|
||||
if ok && it != nil {
|
||||
return it, nil
|
||||
}
|
||||
// assume predeclared type
|
||||
return model.PredeclaredType(v.Name), nil
|
||||
case *ast.InterfaceType:
|
||||
if v.Methods != nil && len(v.Methods.List) > 0 {
|
||||
return nil, p.errorf(v.Pos(), "can't handle non-empty unnamed interface types")
|
||||
}
|
||||
return model.PredeclaredType("any"), nil
|
||||
case *ast.MapType:
|
||||
key, err := p.parseType(pkg, v.Key, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value, err := p.parseType(pkg, v.Value, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.MapType{Key: key, Value: value}, nil
|
||||
case *ast.SelectorExpr:
|
||||
pkgName := v.X.(*ast.Ident).String()
|
||||
pkg, ok := p.imports[pkgName]
|
||||
if !ok {
|
||||
return nil, p.errorf(v.Pos(), "unknown package %q", pkgName)
|
||||
}
|
||||
return &model.NamedType{Package: pkg.Path(), Type: v.Sel.String()}, nil
|
||||
case *ast.StarExpr:
|
||||
t, err := p.parseType(pkg, v.X, tps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.PointerType{Type: t}, nil
|
||||
case *ast.StructType:
|
||||
if v.Fields != nil && len(v.Fields.List) > 0 {
|
||||
return nil, p.errorf(v.Pos(), "can't handle non-empty unnamed struct types")
|
||||
}
|
||||
return model.PredeclaredType("struct{}"), nil
|
||||
case *ast.ParenExpr:
|
||||
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)
|
||||
}
|
||||
|
||||
func (p *fileParser) parseArrayLength(expr ast.Expr) (string, error) {
|
||||
switch val := expr.(type) {
|
||||
case (*ast.BasicLit):
|
||||
return val.Value, nil
|
||||
case (*ast.Ident):
|
||||
// when the length is a const defined locally
|
||||
return val.Obj.Decl.(*ast.ValueSpec).Values[0].(*ast.BasicLit).Value, nil
|
||||
case (*ast.SelectorExpr):
|
||||
// when the length is a const defined in an external package
|
||||
usedPkg, err := importer.Default().Import(fmt.Sprintf("%s", val.X))
|
||||
if err != nil {
|
||||
return "", p.errorf(expr.Pos(), "unknown package in array length: %v", err)
|
||||
}
|
||||
ev, err := types.Eval(token.NewFileSet(), usedPkg, token.NoPos, val.Sel.Name)
|
||||
if err != nil {
|
||||
return "", p.errorf(expr.Pos(), "unknown constant in array length: %v", err)
|
||||
}
|
||||
return ev.Value.String(), nil
|
||||
case (*ast.ParenExpr):
|
||||
return p.parseArrayLength(val.X)
|
||||
case (*ast.BinaryExpr):
|
||||
x, err := p.parseArrayLength(val.X)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
y, err := p.parseArrayLength(val.Y)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
biExpr := fmt.Sprintf("%s%v%s", x, val.Op, y)
|
||||
tv, err := types.Eval(token.NewFileSet(), nil, token.NoPos, biExpr)
|
||||
if err != nil {
|
||||
return "", p.errorf(expr.Pos(), "invalid expression in array length: %v", err)
|
||||
}
|
||||
return tv.Value.String(), nil
|
||||
default:
|
||||
return "", p.errorf(expr.Pos(), "invalid expression in array length: %v", val)
|
||||
}
|
||||
}
|
||||
|
||||
// importsOfFile returns a map of package name to import path
|
||||
// of the imports in file.
|
||||
func importsOfFile(file *ast.File) (normalImports map[string]importedPackage, dotImports []string) {
|
||||
var importPaths []string
|
||||
for _, is := range file.Imports {
|
||||
if is.Name != nil {
|
||||
continue
|
||||
}
|
||||
importPath := is.Path.Value[1 : len(is.Path.Value)-1] // remove quotes
|
||||
importPaths = append(importPaths, importPath)
|
||||
}
|
||||
packagesName := createPackageMap(importPaths)
|
||||
normalImports = make(map[string]importedPackage)
|
||||
dotImports = make([]string, 0)
|
||||
for _, is := range file.Imports {
|
||||
var pkgName string
|
||||
importPath := is.Path.Value[1 : len(is.Path.Value)-1] // remove quotes
|
||||
|
||||
if is.Name != nil {
|
||||
// Named imports are always certain.
|
||||
if is.Name.Name == "_" {
|
||||
continue
|
||||
}
|
||||
pkgName = is.Name.Name
|
||||
} else {
|
||||
pkg, ok := packagesName[importPath]
|
||||
if !ok {
|
||||
// Fallback to import path suffix. Note that this is uncertain.
|
||||
_, last := path.Split(importPath)
|
||||
// If the last path component has dots, the first dot-delimited
|
||||
// field is used as the name.
|
||||
pkgName = strings.SplitN(last, ".", 2)[0]
|
||||
} else {
|
||||
pkgName = pkg
|
||||
}
|
||||
}
|
||||
|
||||
if pkgName == "." {
|
||||
dotImports = append(dotImports, importPath)
|
||||
} else {
|
||||
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}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type namedInterface struct {
|
||||
name *ast.Ident
|
||||
it *ast.InterfaceType
|
||||
typeParams []*ast.Field
|
||||
embeddedInstTypeParams []ast.Expr
|
||||
instTypes []model.Type
|
||||
}
|
||||
|
||||
// Create an iterator over all interfaces in file.
|
||||
func iterInterfaces(file *ast.File) <-chan *namedInterface {
|
||||
ch := make(chan *namedInterface)
|
||||
go func() {
|
||||
for _, decl := range file.Decls {
|
||||
gd, ok := decl.(*ast.GenDecl)
|
||||
if !ok || gd.Tok != token.TYPE {
|
||||
continue
|
||||
}
|
||||
for _, spec := range gd.Specs {
|
||||
ts, ok := spec.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
it, ok := ts.Type.(*ast.InterfaceType)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
ch <- &namedInterface{name: ts.Name, it: it, typeParams: getTypeSpecTypeParams(ts)}
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// isVariadic returns whether the function is variadic.
|
||||
func isVariadic(f *ast.FuncType) bool {
|
||||
nargs := len(f.Params.List)
|
||||
if nargs == 0 {
|
||||
return false
|
||||
}
|
||||
_, ok := f.Params.List[nargs-1].Type.(*ast.Ellipsis)
|
||||
return ok
|
||||
}
|
||||
|
||||
// packageNameOfDir get package import path via dir
|
||||
func packageNameOfDir(srcDir string) (string, error) {
|
||||
files, err := os.ReadDir(srcDir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var goFilePath string
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && strings.HasSuffix(file.Name(), ".go") {
|
||||
goFilePath = file.Name()
|
||||
break
|
||||
}
|
||||
}
|
||||
if goFilePath == "" {
|
||||
return "", fmt.Errorf("go source file not found %s", srcDir)
|
||||
}
|
||||
|
||||
packageImport, err := parsePackageImport(srcDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return packageImport, nil
|
||||
}
|
||||
|
||||
var errOutsideGoPath = errors.New("source directory is outside GOPATH")
|
255
vendor/go.uber.org/mock/mockgen/reflect.go
generated
vendored
Normal file
255
vendor/go.uber.org/mock/mockgen/reflect.go
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
// Copyright 2012 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 main
|
||||
|
||||
// This file contains the model construction by reflection.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"go.uber.org/mock/mockgen/model"
|
||||
)
|
||||
|
||||
var (
|
||||
progOnly = flag.Bool("prog_only", false, "(reflect mode) Only generate the reflection program; write it to stdout and exit.")
|
||||
execOnly = flag.String("exec_only", "", "(reflect mode) If set, execute this reflection program.")
|
||||
buildFlags = flag.String("build_flags", "", "(reflect mode) Additional flags for go build.")
|
||||
)
|
||||
|
||||
// reflectMode generates mocks via reflection on an interface.
|
||||
func reflectMode(importPath string, symbols []string) (*model.Package, error) {
|
||||
if *execOnly != "" {
|
||||
return run(*execOnly)
|
||||
}
|
||||
|
||||
program, err := writeProgram(importPath, symbols)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if *progOnly {
|
||||
if _, err := os.Stdout.Write(program); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
|
||||
// Try to run the reflection program in the current working directory.
|
||||
if p, err := runInDir(program, wd); err == nil {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Try to run the program in the same directory as the input package.
|
||||
if p, err := build.Import(importPath, wd, build.FindOnly); err == nil {
|
||||
dir := p.Dir
|
||||
if p, err := runInDir(program, dir); err == nil {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Try to run it in a standard temp directory.
|
||||
return runInDir(program, "")
|
||||
}
|
||||
|
||||
func writeProgram(importPath string, symbols []string) ([]byte, error) {
|
||||
var program bytes.Buffer
|
||||
data := reflectData{
|
||||
ImportPath: importPath,
|
||||
Symbols: symbols,
|
||||
}
|
||||
if err := reflectProgram.Execute(&program, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return program.Bytes(), nil
|
||||
}
|
||||
|
||||
// run the given program and parse the output as a model.Package.
|
||||
func run(program string) (*model.Package, error) {
|
||||
f, err := os.CreateTemp("", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filename := f.Name()
|
||||
defer os.Remove(filename)
|
||||
if err := f.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Run the program.
|
||||
cmd := exec.Command(program, "-output", filename)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f, err = os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Process output.
|
||||
var pkg model.Package
|
||||
if err := gob.NewDecoder(f).Decode(&pkg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &pkg, nil
|
||||
}
|
||||
|
||||
// runInDir writes the given program into the given dir, runs it there, and
|
||||
// parses the output as a model.Package.
|
||||
func runInDir(program []byte, dir string) (*model.Package, error) {
|
||||
// We use TempDir instead of TempFile so we can control the filename.
|
||||
tmpDir, err := os.MkdirTemp(dir, "gomock_reflect_")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := os.RemoveAll(tmpDir); err != nil {
|
||||
log.Printf("failed to remove temp directory: %s", err)
|
||||
}
|
||||
}()
|
||||
const progSource = "prog.go"
|
||||
var progBinary = "prog.bin"
|
||||
if runtime.GOOS == "windows" {
|
||||
// Windows won't execute a program unless it has a ".exe" suffix.
|
||||
progBinary += ".exe"
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmdArgs := []string{}
|
||||
cmdArgs = append(cmdArgs, "build")
|
||||
if *buildFlags != "" {
|
||||
cmdArgs = append(cmdArgs, strings.Split(*buildFlags, " ")...)
|
||||
}
|
||||
cmdArgs = append(cmdArgs, "-o", progBinary, progSource)
|
||||
|
||||
// Build the program.
|
||||
buf := bytes.NewBuffer(nil)
|
||||
cmd := exec.Command("go", cmdArgs...)
|
||||
cmd.Dir = tmpDir
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = io.MultiWriter(os.Stderr, buf)
|
||||
if err := cmd.Run(); err != nil {
|
||||
sErr := buf.String()
|
||||
if strings.Contains(sErr, `cannot find package "."`) &&
|
||||
strings.Contains(sErr, "go.uber.org/mock/mockgen/model") {
|
||||
fmt.Fprint(os.Stderr, "Please reference the steps in the README to fix this error:\n\thttps://go.uber.org/mock#reflect-vendoring-error.\n")
|
||||
return nil, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return run(filepath.Join(tmpDir, progBinary))
|
||||
}
|
||||
|
||||
type reflectData struct {
|
||||
ImportPath string
|
||||
Symbols []string
|
||||
}
|
||||
|
||||
// This program reflects on an interface value, and prints the
|
||||
// gob encoding of a model.Package to standard output.
|
||||
// JSON doesn't work because of the model.Type interface.
|
||||
var reflectProgram = template.Must(template.New("program").Parse(`
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
|
||||
"go.uber.org/mock/mockgen/model"
|
||||
|
||||
pkg_ {{printf "%q" .ImportPath}}
|
||||
)
|
||||
|
||||
var output = flag.String("output", "", "The output file name, or empty to use stdout.")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
its := []struct{
|
||||
sym string
|
||||
typ reflect.Type
|
||||
}{
|
||||
{{range .Symbols}}
|
||||
{ {{printf "%q" .}}, reflect.TypeOf((*pkg_.{{.}})(nil)).Elem()},
|
||||
{{end}}
|
||||
}
|
||||
pkg := &model.Package{
|
||||
// NOTE: This behaves contrary to documented behaviour if the
|
||||
// package name is not the final component of the import path.
|
||||
// The reflect package doesn't expose the package name, though.
|
||||
Name: path.Base({{printf "%q" .ImportPath}}),
|
||||
}
|
||||
|
||||
for _, it := range its {
|
||||
intf, err := model.InterfaceFromInterfaceType(it.typ)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Reflection: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
intf.Name = it.sym
|
||||
pkg.Interfaces = append(pkg.Interfaces, intf)
|
||||
}
|
||||
|
||||
outfile := os.Stdout
|
||||
if len(*output) != 0 {
|
||||
var err error
|
||||
outfile, err = os.Create(*output)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to open output file %q", *output)
|
||||
}
|
||||
defer func() {
|
||||
if err := outfile.Close(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to close output file %q", *output)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if err := gob.NewEncoder(outfile).Encode(pkg); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gob encode: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
`))
|
31
vendor/go.uber.org/mock/mockgen/version.go
generated
vendored
Normal file
31
vendor/go.uber.org/mock/mockgen/version.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func printModuleVersion() {
|
||||
if bi, exists := debug.ReadBuildInfo(); exists {
|
||||
fmt.Println(bi.Main.Version)
|
||||
} else {
|
||||
log.Printf("No version information found. Make sure to use " +
|
||||
"GO111MODULE=on when running 'go get' in order to use specific " +
|
||||
"version of the binary.")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user