Path: blob/main/vendor/github.com/golang/mock/mockgen/model/model.go
2893 views
// Copyright 2012 Google Inc.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314// Package model contains the data model necessary for generating mock implementations.15package model1617import (18"encoding/gob"19"fmt"20"io"21"reflect"22"strings"23)2425// pkgPath is the importable path for package model26const pkgPath = "github.com/golang/mock/mockgen/model"2728// Package is a Go package. It may be a subset.29type Package struct {30Name string31PkgPath string32Interfaces []*Interface33DotImports []string34}3536// Print writes the package name and its exported interfaces.37func (pkg *Package) Print(w io.Writer) {38_, _ = fmt.Fprintf(w, "package %s\n", pkg.Name)39for _, intf := range pkg.Interfaces {40intf.Print(w)41}42}4344// Imports returns the imports needed by the Package as a set of import paths.45func (pkg *Package) Imports() map[string]bool {46im := make(map[string]bool)47for _, intf := range pkg.Interfaces {48intf.addImports(im)49}50return im51}5253// Interface is a Go interface.54type Interface struct {55Name string56Methods []*Method57}5859// Print writes the interface name and its methods.60func (intf *Interface) Print(w io.Writer) {61_, _ = fmt.Fprintf(w, "interface %s\n", intf.Name)62for _, m := range intf.Methods {63m.Print(w)64}65}6667func (intf *Interface) addImports(im map[string]bool) {68for _, m := range intf.Methods {69m.addImports(im)70}71}7273// AddMethod adds a new method, de-duplicating by method name.74func (intf *Interface) AddMethod(m *Method) {75for _, me := range intf.Methods {76if me.Name == m.Name {77return78}79}80intf.Methods = append(intf.Methods, m)81}8283// Method is a single method of an interface.84type Method struct {85Name string86In, Out []*Parameter87Variadic *Parameter // may be nil88}8990// Print writes the method name and its signature.91func (m *Method) Print(w io.Writer) {92_, _ = fmt.Fprintf(w, " - method %s\n", m.Name)93if len(m.In) > 0 {94_, _ = fmt.Fprintf(w, " in:\n")95for _, p := range m.In {96p.Print(w)97}98}99if m.Variadic != nil {100_, _ = fmt.Fprintf(w, " ...:\n")101m.Variadic.Print(w)102}103if len(m.Out) > 0 {104_, _ = fmt.Fprintf(w, " out:\n")105for _, p := range m.Out {106p.Print(w)107}108}109}110111func (m *Method) addImports(im map[string]bool) {112for _, p := range m.In {113p.Type.addImports(im)114}115if m.Variadic != nil {116m.Variadic.Type.addImports(im)117}118for _, p := range m.Out {119p.Type.addImports(im)120}121}122123// Parameter is an argument or return parameter of a method.124type Parameter struct {125Name string // may be empty126Type Type127}128129// Print writes a method parameter.130func (p *Parameter) Print(w io.Writer) {131n := p.Name132if n == "" {133n = `""`134}135_, _ = fmt.Fprintf(w, " - %v: %v\n", n, p.Type.String(nil, ""))136}137138// Type is a Go type.139type Type interface {140String(pm map[string]string, pkgOverride string) string141addImports(im map[string]bool)142}143144func init() {145gob.Register(&ArrayType{})146gob.Register(&ChanType{})147gob.Register(&FuncType{})148gob.Register(&MapType{})149gob.Register(&NamedType{})150gob.Register(&PointerType{})151152// Call gob.RegisterName to make sure it has the consistent name registered153// for both gob decoder and encoder.154//155// For a non-pointer type, gob.Register will try to get package full path by156// calling rt.PkgPath() for a name to register. If your project has vendor157// directory, it is possible that PkgPath will get a path like this:158// ../../../vendor/github.com/golang/mock/mockgen/model159gob.RegisterName(pkgPath+".PredeclaredType", PredeclaredType(""))160}161162// ArrayType is an array or slice type.163type ArrayType struct {164Len int // -1 for slices, >= 0 for arrays165Type Type166}167168func (at *ArrayType) String(pm map[string]string, pkgOverride string) string {169s := "[]"170if at.Len > -1 {171s = fmt.Sprintf("[%d]", at.Len)172}173return s + at.Type.String(pm, pkgOverride)174}175176func (at *ArrayType) addImports(im map[string]bool) { at.Type.addImports(im) }177178// ChanType is a channel type.179type ChanType struct {180Dir ChanDir // 0, 1 or 2181Type Type182}183184func (ct *ChanType) String(pm map[string]string, pkgOverride string) string {185s := ct.Type.String(pm, pkgOverride)186if ct.Dir == RecvDir {187return "<-chan " + s188}189if ct.Dir == SendDir {190return "chan<- " + s191}192return "chan " + s193}194195func (ct *ChanType) addImports(im map[string]bool) { ct.Type.addImports(im) }196197// ChanDir is a channel direction.198type ChanDir int199200// Constants for channel directions.201const (202RecvDir ChanDir = 1203SendDir ChanDir = 2204)205206// FuncType is a function type.207type FuncType struct {208In, Out []*Parameter209Variadic *Parameter // may be nil210}211212func (ft *FuncType) String(pm map[string]string, pkgOverride string) string {213args := make([]string, len(ft.In))214for i, p := range ft.In {215args[i] = p.Type.String(pm, pkgOverride)216}217if ft.Variadic != nil {218args = append(args, "..."+ft.Variadic.Type.String(pm, pkgOverride))219}220rets := make([]string, len(ft.Out))221for i, p := range ft.Out {222rets[i] = p.Type.String(pm, pkgOverride)223}224retString := strings.Join(rets, ", ")225if nOut := len(ft.Out); nOut == 1 {226retString = " " + retString227} else if nOut > 1 {228retString = " (" + retString + ")"229}230return "func(" + strings.Join(args, ", ") + ")" + retString231}232233func (ft *FuncType) addImports(im map[string]bool) {234for _, p := range ft.In {235p.Type.addImports(im)236}237if ft.Variadic != nil {238ft.Variadic.Type.addImports(im)239}240for _, p := range ft.Out {241p.Type.addImports(im)242}243}244245// MapType is a map type.246type MapType struct {247Key, Value Type248}249250func (mt *MapType) String(pm map[string]string, pkgOverride string) string {251return "map[" + mt.Key.String(pm, pkgOverride) + "]" + mt.Value.String(pm, pkgOverride)252}253254func (mt *MapType) addImports(im map[string]bool) {255mt.Key.addImports(im)256mt.Value.addImports(im)257}258259// NamedType is an exported type in a package.260type NamedType struct {261Package string // may be empty262Type string263}264265func (nt *NamedType) String(pm map[string]string, pkgOverride string) string {266if pkgOverride == nt.Package {267return nt.Type268}269prefix := pm[nt.Package]270if prefix != "" {271return prefix + "." + nt.Type272}273274return nt.Type275}276277func (nt *NamedType) addImports(im map[string]bool) {278if nt.Package != "" {279im[nt.Package] = true280}281}282283// PointerType is a pointer to another type.284type PointerType struct {285Type Type286}287288func (pt *PointerType) String(pm map[string]string, pkgOverride string) string {289return "*" + pt.Type.String(pm, pkgOverride)290}291func (pt *PointerType) addImports(im map[string]bool) { pt.Type.addImports(im) }292293// PredeclaredType is a predeclared type such as "int".294type PredeclaredType string295296func (pt PredeclaredType) String(map[string]string, string) string { return string(pt) }297func (pt PredeclaredType) addImports(map[string]bool) {}298299// The following code is intended to be called by the program generated by ../reflect.go.300301// InterfaceFromInterfaceType returns a pointer to an interface for the302// given reflection interface type.303func InterfaceFromInterfaceType(it reflect.Type) (*Interface, error) {304if it.Kind() != reflect.Interface {305return nil, fmt.Errorf("%v is not an interface", it)306}307intf := &Interface{}308309for i := 0; i < it.NumMethod(); i++ {310mt := it.Method(i)311// TODO: need to skip unexported methods? or just raise an error?312m := &Method{313Name: mt.Name,314}315316var err error317m.In, m.Variadic, m.Out, err = funcArgsFromType(mt.Type)318if err != nil {319return nil, err320}321322intf.AddMethod(m)323}324325return intf, nil326}327328// t's Kind must be a reflect.Func.329func funcArgsFromType(t reflect.Type) (in []*Parameter, variadic *Parameter, out []*Parameter, err error) {330nin := t.NumIn()331if t.IsVariadic() {332nin--333}334var p *Parameter335for i := 0; i < nin; i++ {336p, err = parameterFromType(t.In(i))337if err != nil {338return339}340in = append(in, p)341}342if t.IsVariadic() {343p, err = parameterFromType(t.In(nin).Elem())344if err != nil {345return346}347variadic = p348}349for i := 0; i < t.NumOut(); i++ {350p, err = parameterFromType(t.Out(i))351if err != nil {352return353}354out = append(out, p)355}356return357}358359func parameterFromType(t reflect.Type) (*Parameter, error) {360tt, err := typeFromType(t)361if err != nil {362return nil, err363}364return &Parameter{Type: tt}, nil365}366367var errorType = reflect.TypeOf((*error)(nil)).Elem()368369var byteType = reflect.TypeOf(byte(0))370371func typeFromType(t reflect.Type) (Type, error) {372// Hack workaround for https://golang.org/issue/3853.373// This explicit check should not be necessary.374if t == byteType {375return PredeclaredType("byte"), nil376}377378if imp := t.PkgPath(); imp != "" {379return &NamedType{380Package: impPath(imp),381Type: t.Name(),382}, nil383}384385// only unnamed or predeclared types after here386387// Lots of types have element types. Let's do the parsing and error checking for all of them.388var elemType Type389switch t.Kind() {390case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice:391var err error392elemType, err = typeFromType(t.Elem())393if err != nil {394return nil, err395}396}397398switch t.Kind() {399case reflect.Array:400return &ArrayType{401Len: t.Len(),402Type: elemType,403}, nil404case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,405reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,406reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String:407return PredeclaredType(t.Kind().String()), nil408case reflect.Chan:409var dir ChanDir410switch t.ChanDir() {411case reflect.RecvDir:412dir = RecvDir413case reflect.SendDir:414dir = SendDir415}416return &ChanType{417Dir: dir,418Type: elemType,419}, nil420case reflect.Func:421in, variadic, out, err := funcArgsFromType(t)422if err != nil {423return nil, err424}425return &FuncType{426In: in,427Out: out,428Variadic: variadic,429}, nil430case reflect.Interface:431// Two special interfaces.432if t.NumMethod() == 0 {433return PredeclaredType("interface{}"), nil434}435if t == errorType {436return PredeclaredType("error"), nil437}438case reflect.Map:439kt, err := typeFromType(t.Key())440if err != nil {441return nil, err442}443return &MapType{444Key: kt,445Value: elemType,446}, nil447case reflect.Ptr:448return &PointerType{449Type: elemType,450}, nil451case reflect.Slice:452return &ArrayType{453Len: -1,454Type: elemType,455}, nil456case reflect.Struct:457if t.NumField() == 0 {458return PredeclaredType("struct{}"), nil459}460}461462// TODO: Struct, UnsafePointer463return nil, fmt.Errorf("can't yet turn %v (%v) into a model.Type", t, t.Kind())464}465466// impPath sanitizes the package path returned by `PkgPath` method of a reflect Type so that467// it is importable. PkgPath might return a path that includes "vendor". These paths do not468// compile, so we need to remove everything up to and including "/vendor/".469// See https://github.com/golang/go/issues/12019.470func impPath(imp string) string {471if strings.HasPrefix(imp, "vendor/") {472imp = "/" + imp473}474if i := strings.LastIndex(imp, "/vendor/"); i != -1 {475imp = imp[i+len("/vendor/"):]476}477return imp478}479480// ErrorInterface represent built-in error interface.481var ErrorInterface = Interface{482Name: "error",483Methods: []*Method{484{485Name: "Error",486Out: []*Parameter{487{488Name: "",489Type: PredeclaredType("string"),490},491},492},493},494}495496497