moq/internal/registry/var.go
dedalusj 514e30653d
Fix issue with custom types shadowing basic types (#163)
When a custom type has a name that can shadow an in-built type, append
MoqParam to the name. Example: var name stringMoqParam for null.String.
2021-12-18 08:56:01 +05:30

147 lines
3.1 KiB
Go

package registry
import (
"go/types"
"strings"
)
// Var represents a method variable/parameter.
//
// It should be created using a method scope instance.
type Var struct {
vr *types.Var
imports map[string]*Package
moqPkgPath string
Name string
}
// IsSlice returns whether the type (or the underlying type) is a slice.
func (v Var) IsSlice() bool {
_, ok := v.vr.Type().Underlying().(*types.Slice)
return ok
}
// TypeString returns the variable type with the package qualifier in the
// format 'pkg.Type'.
func (v Var) TypeString() string {
return types.TypeString(v.vr.Type(), v.packageQualifier)
}
// packageQualifier is a types.Qualifier.
func (v Var) packageQualifier(pkg *types.Package) string {
path := stripVendorPath(pkg.Path())
if v.moqPkgPath != "" && v.moqPkgPath == path {
return ""
}
return v.imports[path].Qualifier()
}
func varName(vr *types.Var, suffix string) string {
name := vr.Name()
if name != "" && name != "_" {
return name + suffix
}
name = varNameForType(vr.Type()) + suffix
switch name {
case "mock", "callInfo", "break", "default", "func", "interface", "select", "case", "defer", "go", "map", "struct",
"chan", "else", "goto", "package", "switch", "const", "fallthrough", "if", "range", "type", "continue", "for",
"import", "return", "var",
// avoid shadowing basic types
"string", "bool", "byte", "rune", "uintptr",
"int", "int8", "int16", "int32", "int64",
"uint", "uint8", "uint16", "uint32", "uint64",
"float32", "float64", "complex64", "complex128":
name += "MoqParam"
}
return name
}
// varNameForType generates a name for the variable using the type
// information.
//
// Examples:
// - string -> s
// - int -> n
// - chan int -> intCh
// - []a.MyType -> myTypes
// - map[string]int -> stringToInt
// - error -> err
// - a.MyType -> myType
func varNameForType(t types.Type) string {
nestedType := func(t types.Type) string {
if t, ok := t.(*types.Basic); ok {
return deCapitalise(t.String())
}
return varNameForType(t)
}
switch t := t.(type) {
case *types.Named:
if t.Obj().Name() == "error" {
return "err"
}
name := deCapitalise(t.Obj().Name())
if name == t.Obj().Name() {
name += "MoqParam"
}
return name
case *types.Basic:
return basicTypeVarName(t)
case *types.Array:
return nestedType(t.Elem()) + "s"
case *types.Slice:
return nestedType(t.Elem()) + "s"
case *types.Struct: // anonymous struct
return "val"
case *types.Pointer:
return varNameForType(t.Elem())
case *types.Signature:
return "fn"
case *types.Interface: // anonymous interface
return "ifaceVal"
case *types.Map:
return nestedType(t.Key()) + "To" + capitalise(nestedType(t.Elem()))
case *types.Chan:
return nestedType(t.Elem()) + "Ch"
}
return "v"
}
func basicTypeVarName(b *types.Basic) string {
switch b.Info() {
case types.IsBoolean:
return "b"
case types.IsInteger:
return "n"
case types.IsFloat:
return "f"
case types.IsString:
return "s"
}
return "v"
}
func capitalise(s string) string { return strings.ToUpper(s[:1]) + s[1:] }
func deCapitalise(s string) string { return strings.ToLower(s[:1]) + s[1:] }