refactor(validation-gen): streamline type validation logic

- Consolidated type validation checks into a new  function for improved readability and maintainability.
- Updated the  method to utilize the new validation function, simplifying error handling for unsupported types.
- Removed redundant code related to pointer and map validations, enhancing clarity in the validation process.
- Adjusted test cases in  to reflect changes in type handling and ensure consistency with the new validation logic.

Co-authored-by: Tim Hockin <thockin@google.com>
Co-authored-by: Aaron Prindle <aprindle@google.com>
Co-authored-by: Joe Betz <jpbetz@google.com>
This commit is contained in:
yongruilin
2025-07-12 04:42:46 +00:00
parent 7ad89e1997
commit 0b1fa64a92
3 changed files with 203 additions and 210 deletions

View File

@@ -66,7 +66,8 @@ func IsNilableType(t *types.Type) bool {
// - given `type X []int; *X`, returns `*[]int`
func NativeType(t *types.Type) *types.Type {
ptrs := 0
for {
conditionMet := false
for !conditionMet {
switch t.Kind {
case types.Alias:
t = t.Underlying
@@ -74,10 +75,9 @@ func NativeType(t *types.Type) *types.Type {
ptrs++
t = t.Elem
default:
goto done
conditionMet = true
}
}
done:
for range ptrs {
t = types.PointerTo(t)
}

View File

@@ -237,56 +237,10 @@ func (td *typeDiscoverer) discoverType(t *types.Type, fldPath *field.Path) (*typ
}
}
// Catch some edge cases that we don't want to handle (yet?). This happens
// Catch some cases that we don't want to handle (yet?). This happens
// as early as possible to make all the other code simpler.
switch t.Kind {
case types.Builtin, types.Struct:
// Allowed
case types.Interface:
// We can't do much with interfaces, but they pop up in some places
// like RawExtension.
case types.Alias:
if t.Underlying.Kind == types.Pointer {
return nil, fmt.Errorf("field %s (%s): typedefs to pointers are not supported", fldPath.String(), t)
}
case types.Pointer:
pointee := util.NativeType(t.Elem)
switch pointee.Kind {
case types.Pointer:
return nil, fmt.Errorf("field %s (%s): pointers to pointers are not supported", fldPath.String(), t)
case types.Slice, types.Array:
return nil, fmt.Errorf("field %s (%s): pointers to lists are not supported", fldPath.String(), t)
case types.Map:
return nil, fmt.Errorf("field %s (%s): pointers to maps are not supported", fldPath.String(), t)
}
case types.Array:
return nil, fmt.Errorf("field %s (%s): fixed-size arrays are not supported", fldPath.String(), t)
case types.Slice:
elem := util.NativeType(t.Elem)
switch elem.Kind {
case types.Pointer:
return nil, fmt.Errorf("field %s (%s): lists of pointers are not supported", fldPath.String(), t)
case types.Slice:
if util.NativeType(elem.Elem) != types.Byte {
return nil, fmt.Errorf("field %s (%s): lists of lists are not supported", fldPath.String(), t)
}
case types.Map:
return nil, fmt.Errorf("field %s (%s): lists of maps are not supported", fldPath.String(), t)
}
case types.Map:
key := util.NativeType(t.Key)
if key != types.String {
return nil, fmt.Errorf("field %s (%s): maps with non-string keys are not supported", fldPath.String(), t)
}
elem := util.NativeType(t.Elem)
switch elem.Kind {
case types.Pointer:
return nil, fmt.Errorf("field %s (%s): maps of pointers are not supported", fldPath.String(), t)
case types.Map:
return nil, fmt.Errorf("field %s (%s): maps of maps are not supported", fldPath.String(), t)
}
default:
return nil, fmt.Errorf("field %s (%v, kind %v) is not supported", fldPath.String(), t, t.Kind)
if err := td.verifySupportedType(t); err != nil {
return nil, fmt.Errorf("field %s (%s): %w", fldPath.String(), t, err)
}
// Discovery applies to values, not pointers.
@@ -410,11 +364,7 @@ func (td *typeDiscoverer) discoverType(t *types.Type, fldPath *field.Path) (*typ
} else if validations.Empty() {
klog.V(6).InfoS("no type-attached validations", "type", t)
} else {
// TODO: This check is placed here to allow map-of-slices fields to exist in the type
// system, while preventing validation on them since that functionality is not yet
// implemented. Once validation support for map-of-slices is added, this check should
// be removed.
if util.NativeType(t).Kind == types.Map && util.NativeType(t).Elem.Kind == types.Slice {
if util.NonPointer(util.NativeType(t)).Kind == types.Map && util.NonPointer(util.NativeType(t)).Elem.Kind == types.Slice {
return nil, fmt.Errorf("field %s: validation for map of slices is not supported", fldPath)
}
klog.V(5).InfoS("found type-attached validations", "n", validations.Len(), "type", t)
@@ -532,6 +482,61 @@ func (td *typeDiscoverer) discoverType(t *types.Type, fldPath *field.Path) (*typ
return thisNode, nil
}
// verifySupportedType checks whether the given type is supported.
func (td *typeDiscoverer) verifySupportedType(t *types.Type) error {
switch t.Kind {
case types.Builtin, types.Struct:
// Allowed
case types.Interface:
// We can't do much with interfaces, but they pop up in some places
// like RawExtension.
case types.Alias:
if t.Underlying.Kind == types.Pointer {
return fmt.Errorf("typedefs to pointers are not supported")
}
case types.Pointer:
pointee := util.NativeType(t.Elem)
switch pointee.Kind {
case types.Pointer:
return fmt.Errorf("pointers to pointers are not supported")
case types.Slice, types.Array:
return fmt.Errorf("pointers to lists are not supported")
case types.Map:
return fmt.Errorf("pointers to maps are not supported")
}
case types.Array:
return fmt.Errorf("fixed-size arrays are not supported")
case types.Slice:
elem := util.NativeType(t.Elem)
switch elem.Kind {
case types.Pointer:
return fmt.Errorf("lists of pointers are not supported")
case types.Slice:
if util.NativeType(elem.Elem) != types.Byte {
return fmt.Errorf("lists of lists are not supported")
}
case types.Map:
return fmt.Errorf("lists of maps are not supported")
}
case types.Map:
key := util.NativeType(t.Key)
if key != types.String {
return fmt.Errorf("maps with non-string keys are not supported")
}
elem := util.NativeType(t.Elem)
switch elem.Kind {
case types.Pointer:
return fmt.Errorf("maps of pointers are not supported")
case types.Map:
return fmt.Errorf("maps of maps are not supported")
}
default:
return fmt.Errorf("kind %v is not supported", t.Kind)
}
return nil
}
// discoverStruct walks a struct type recursively.
func (td *typeDiscoverer) discoverStruct(thisNode *typeNode, fldPath *field.Path) error {
var fields []*childNode
@@ -589,11 +594,7 @@ func (td *typeDiscoverer) discoverStruct(thisNode *typeNode, fldPath *field.Path
klog.V(6).InfoS("no field-attached validations", "field", childPath)
} else {
klog.V(5).InfoS("found field-attached validations", "n", validations.Len(), "field", childPath)
// TODO: This check is placed here to allow map-of-slices fields to exist in the type
// system, while preventing validation on them since that functionality is not yet
// implemented. Once validation support for map-of-slices is added, this check should
// be removed.
if util.NativeType(childType).Kind == types.Map && util.NativeType(childType).Elem.Kind == types.Slice {
if util.NonPointer(util.NativeType(childType)).Kind == types.Map && util.NonPointer(util.NativeType(childType)).Elem.Kind == types.Slice {
return fmt.Errorf("field %s: validation for map of slices is not supported", childPath)
}
child.fieldValidations.Add(validations)
@@ -603,7 +604,7 @@ func (td *typeDiscoverer) discoverStruct(thisNode *typeNode, fldPath *field.Path
}
// Handle non-included types.
switch nonPtrType(childType).Kind {
switch util.NonPointer(childType).Kind {
case types.Struct, types.Alias:
if child.node == nil { // a non-included type
if !child.fieldValidations.OpaqueType {
@@ -728,14 +729,6 @@ func (td *typeDiscoverer) discoverStruct(thisNode *typeNode, fldPath *field.Path
return nil
}
// nonPtrType removes any pointerness from the type.
func nonPtrType(t *types.Type) *types.Type {
for t.Kind == types.Pointer {
t = t.Elem
}
return t
}
// getValidationFunctionName looks up the name of the specified type's
// validation function.
//
@@ -1009,8 +1002,6 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild
bufsw := sw.Dup(buf)
validations := fld.fieldValidations
// fldRatchetingChecked is used to avoid emitting the ratcheting check multiple times.
fldRatchetingChecked := false
if !validations.Empty() {
emitComments(validations.Comments, bufsw)
@@ -1146,9 +1137,15 @@ func emitRatchetingCheck(c *generator.Context, t *types.Type, sw *generator.Snip
}
// If the type is a builtin, we can use a simpler equality check when they are not nil.
if util.IsDirectComparable(util.NonPointer(util.NativeType(t))) {
_, exprPfx, _ := getLeafTypeAndPrefixes(t)
targs["exprPfx"] = exprPfx
sw.Do("if op.Type == $.operation.Update|raw$ && (obj == oldObj || (obj != nil && oldObj != nil && $.exprPfx$obj == $.exprPfx$oldObj)) {\n", targs)
// We should never get anything but pointers here, since every other
// nilable type is not Comparable.
//
// This condition looks overly complex, but each case is needed:
// - obj == oldObj : handle pointers which are nil in old and new
// - obj != nil : handle optional fields which are updated to nil
// - oldObj != nil : handle optional fields which are updated from nil
// - *obj == *oldObj : compare values
sw.Do("if op.Type == $.operation.Update|raw$ && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {\n", targs)
} else {
targs["equality"] = mkSymbolArgs(c, equalityPkgSymbols)
sw.Do("if op.Type == $.operation.Update|raw$ && $.equality.Semantic|raw$.DeepEqual(obj, oldObj) {\n", targs)
@@ -1341,6 +1338,8 @@ func toGolangSourceDataLiteral(sw *generator.SnippetWriter, c *generator.Context
sw.Do("$.|raw$", v)
case types.Member:
sw.Do("obj."+v.Name, nil)
case *types.Member:
sw.Do("obj."+v.Name, nil)
case validators.Identifier:
sw.Do("$.|raw$", c.Universe.Type(types.Name(v)))
case *validators.Identifier:

View File

@@ -22,59 +22,53 @@ import (
"k8s.io/gengo/v2/types"
)
func TestGetLeafTypeAndPrefixes(t *testing.T) {
stringType := &types.Type{
// gengo has `PointerTo()` but not the rest, so keep this here for consistency.
func ptrTo(t *types.Type) *types.Type {
return &types.Type{
Name: types.Name{
Package: "",
Name: "string",
Name: "*" + t.Name.String(),
},
Kind: types.Builtin,
Kind: types.Pointer,
Elem: t,
}
}
ptrTo := func(t *types.Type) *types.Type {
return &types.Type{
Name: types.Name{
Package: "",
Name: "*" + t.Name.String(),
},
Kind: types.Pointer,
Elem: t,
}
func sliceOf(t *types.Type) *types.Type {
return &types.Type{
Name: types.Name{
Package: "",
Name: "[]" + t.Name.String(),
},
Kind: types.Slice,
Elem: t,
}
}
sliceOf := func(t *types.Type) *types.Type {
return &types.Type{
Name: types.Name{
Package: "",
Name: "[]" + t.Name.String(),
},
Kind: types.Slice,
Elem: t,
}
func mapOf(t *types.Type) *types.Type {
return &types.Type{
Name: types.Name{
Package: "",
Name: "map[string]" + t.Name.String(),
},
Kind: types.Map,
Key: types.String,
Elem: t,
}
}
mapOf := func(t *types.Type) *types.Type {
return &types.Type{
Name: types.Name{
Package: "",
Name: "map[string]" + t.Name.String(),
},
Kind: types.Map,
Key: stringType,
Elem: t,
}
func aliasOf(name string, t *types.Type) *types.Type {
return &types.Type{
Name: types.Name{
Package: "",
Name: "Alias_" + name,
},
Kind: types.Alias,
Underlying: t,
}
}
aliasOf := func(name string, t *types.Type) *types.Type {
return &types.Type{
Name: types.Name{
Package: "",
Name: "Alias_" + name,
},
Kind: types.Alias,
Underlying: t,
}
}
func TestGetLeafTypeAndPrefixes(t *testing.T) {
cases := []struct {
in *types.Type
@@ -83,278 +77,278 @@ func TestGetLeafTypeAndPrefixes(t *testing.T) {
expectedExprPfx string
}{{
// string
in: stringType,
expectedType: stringType,
in: types.String,
expectedType: types.String,
expectedTypePfx: "*",
expectedExprPfx: "&",
}, {
// *string
in: ptrTo(stringType),
expectedType: stringType,
in: ptrTo(types.String),
expectedType: types.String,
expectedTypePfx: "*",
expectedExprPfx: "",
}, {
// **string
in: ptrTo(ptrTo(stringType)),
expectedType: stringType,
in: ptrTo(ptrTo(types.String)),
expectedType: types.String,
expectedTypePfx: "*",
expectedExprPfx: "*",
}, {
// ***string
in: ptrTo(ptrTo(ptrTo(stringType))),
expectedType: stringType,
in: ptrTo(ptrTo(ptrTo(types.String))),
expectedType: types.String,
expectedTypePfx: "*",
expectedExprPfx: "**",
}, {
// []string
in: sliceOf(stringType),
expectedType: sliceOf(stringType),
in: sliceOf(types.String),
expectedType: sliceOf(types.String),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// *[]string
in: ptrTo(sliceOf(stringType)),
expectedType: sliceOf(stringType),
in: ptrTo(sliceOf(types.String)),
expectedType: sliceOf(types.String),
expectedTypePfx: "",
expectedExprPfx: "*",
}, {
// **[]string
in: ptrTo(ptrTo(sliceOf(stringType))),
expectedType: sliceOf(stringType),
in: ptrTo(ptrTo(sliceOf(types.String))),
expectedType: sliceOf(types.String),
expectedTypePfx: "",
expectedExprPfx: "**",
}, {
// ***[]string
in: ptrTo(ptrTo(ptrTo(sliceOf(stringType)))),
expectedType: sliceOf(stringType),
in: ptrTo(ptrTo(ptrTo(sliceOf(types.String)))),
expectedType: sliceOf(types.String),
expectedTypePfx: "",
expectedExprPfx: "***",
}, {
// map[string]string
in: mapOf(stringType),
expectedType: mapOf(stringType),
in: mapOf(types.String),
expectedType: mapOf(types.String),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// *map[string]string
in: ptrTo(mapOf(stringType)),
expectedType: mapOf(stringType),
in: ptrTo(mapOf(types.String)),
expectedType: mapOf(types.String),
expectedTypePfx: "",
expectedExprPfx: "*",
}, {
// **map[string]string
in: ptrTo(ptrTo(mapOf(stringType))),
expectedType: mapOf(stringType),
in: ptrTo(ptrTo(mapOf(types.String))),
expectedType: mapOf(types.String),
expectedTypePfx: "",
expectedExprPfx: "**",
}, {
// ***map[string]string
in: ptrTo(ptrTo(ptrTo(mapOf(stringType)))),
expectedType: mapOf(stringType),
in: ptrTo(ptrTo(ptrTo(mapOf(types.String)))),
expectedType: mapOf(types.String),
expectedTypePfx: "",
expectedExprPfx: "***",
}, {
// alias of string
in: aliasOf("s", stringType),
expectedType: aliasOf("s", stringType),
in: aliasOf("s", types.String),
expectedType: aliasOf("s", types.String),
expectedTypePfx: "*",
expectedExprPfx: "&",
}, {
// alias of *string
in: aliasOf("ps", ptrTo(stringType)),
expectedType: aliasOf("ps", stringType),
in: aliasOf("ps", ptrTo(types.String)),
expectedType: aliasOf("ps", types.String),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// alias of **string
in: aliasOf("pps", ptrTo(ptrTo(stringType))),
expectedType: aliasOf("pps", stringType),
in: aliasOf("pps", ptrTo(ptrTo(types.String))),
expectedType: aliasOf("pps", types.String),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// alias of ***string
in: aliasOf("ppps", ptrTo(ptrTo(ptrTo(stringType)))),
expectedType: aliasOf("ppps", stringType),
in: aliasOf("ppps", ptrTo(ptrTo(ptrTo(types.String)))),
expectedType: aliasOf("ppps", types.String),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// alias of []string
in: aliasOf("ls", sliceOf(stringType)),
expectedType: aliasOf("ls", sliceOf(stringType)),
in: aliasOf("ls", sliceOf(types.String)),
expectedType: aliasOf("ls", sliceOf(types.String)),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// alias of *[]string
in: aliasOf("pls", ptrTo(sliceOf(stringType))),
expectedType: aliasOf("pls", sliceOf(stringType)),
in: aliasOf("pls", ptrTo(sliceOf(types.String))),
expectedType: aliasOf("pls", sliceOf(types.String)),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// alias of **[]string
in: aliasOf("ppls", ptrTo(ptrTo(sliceOf(stringType)))),
expectedType: aliasOf("ppls", sliceOf(stringType)),
in: aliasOf("ppls", ptrTo(ptrTo(sliceOf(types.String)))),
expectedType: aliasOf("ppls", sliceOf(types.String)),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// alias of ***[]string
in: aliasOf("pppls", ptrTo(ptrTo(ptrTo(sliceOf(stringType))))),
expectedType: aliasOf("pppls", sliceOf(stringType)),
in: aliasOf("pppls", ptrTo(ptrTo(ptrTo(sliceOf(types.String))))),
expectedType: aliasOf("pppls", sliceOf(types.String)),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// alias of map[string]string
in: aliasOf("ms", mapOf(stringType)),
expectedType: aliasOf("ms", mapOf(stringType)),
in: aliasOf("ms", mapOf(types.String)),
expectedType: aliasOf("ms", mapOf(types.String)),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// alias of *map[string]string
in: aliasOf("pms", ptrTo(mapOf(stringType))),
expectedType: aliasOf("pms", mapOf(stringType)),
in: aliasOf("pms", ptrTo(mapOf(types.String))),
expectedType: aliasOf("pms", mapOf(types.String)),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// alias of **map[string]string
in: aliasOf("ppms", ptrTo(ptrTo(mapOf(stringType)))),
expectedType: aliasOf("ppms", mapOf(stringType)),
in: aliasOf("ppms", ptrTo(ptrTo(mapOf(types.String)))),
expectedType: aliasOf("ppms", mapOf(types.String)),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// alias of ***map[string]string
in: aliasOf("pppms", ptrTo(ptrTo(ptrTo(mapOf(stringType))))),
expectedType: aliasOf("pppms", mapOf(stringType)),
in: aliasOf("pppms", ptrTo(ptrTo(ptrTo(mapOf(types.String))))),
expectedType: aliasOf("pppms", mapOf(types.String)),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// *alias-of-string
in: ptrTo(aliasOf("s", stringType)),
expectedType: aliasOf("s", stringType),
in: ptrTo(aliasOf("s", types.String)),
expectedType: aliasOf("s", types.String),
expectedTypePfx: "*",
expectedExprPfx: "",
}, {
// **alias-of-string
in: ptrTo(ptrTo(aliasOf("s", stringType))),
expectedType: aliasOf("s", stringType),
in: ptrTo(ptrTo(aliasOf("s", types.String))),
expectedType: aliasOf("s", types.String),
expectedTypePfx: "*",
expectedExprPfx: "*",
}, {
// ***alias-of-string
in: ptrTo(ptrTo(ptrTo(aliasOf("s", stringType)))),
expectedType: aliasOf("s", stringType),
in: ptrTo(ptrTo(ptrTo(aliasOf("s", types.String)))),
expectedType: aliasOf("s", types.String),
expectedTypePfx: "*",
expectedExprPfx: "**",
}, {
// []alias-of-string
in: sliceOf(aliasOf("s", stringType)),
expectedType: sliceOf(aliasOf("s", stringType)),
in: sliceOf(aliasOf("s", types.String)),
expectedType: sliceOf(aliasOf("s", types.String)),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// *[]alias-of-string
in: ptrTo(sliceOf(aliasOf("s", stringType))),
expectedType: sliceOf(aliasOf("s", stringType)),
in: ptrTo(sliceOf(aliasOf("s", types.String))),
expectedType: sliceOf(aliasOf("s", types.String)),
expectedTypePfx: "",
expectedExprPfx: "*",
}, {
// **[]alias-of-string
in: ptrTo(ptrTo(sliceOf(aliasOf("s", stringType)))),
expectedType: sliceOf(aliasOf("s", stringType)),
in: ptrTo(ptrTo(sliceOf(aliasOf("s", types.String)))),
expectedType: sliceOf(aliasOf("s", types.String)),
expectedTypePfx: "",
expectedExprPfx: "**",
}, {
// ***[]alias-of-string
in: ptrTo(ptrTo(ptrTo(sliceOf(aliasOf("s", stringType))))),
expectedType: sliceOf(aliasOf("s", stringType)),
in: ptrTo(ptrTo(ptrTo(sliceOf(aliasOf("s", types.String))))),
expectedType: sliceOf(aliasOf("s", types.String)),
expectedTypePfx: "",
expectedExprPfx: "***",
}, {
// map[string]alias-of-string
in: mapOf(aliasOf("s", stringType)),
expectedType: mapOf(aliasOf("s", stringType)),
in: mapOf(aliasOf("s", types.String)),
expectedType: mapOf(aliasOf("s", types.String)),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// *map[string]alias-of-string
in: ptrTo(mapOf(aliasOf("s", stringType))),
expectedType: mapOf(aliasOf("s", stringType)),
in: ptrTo(mapOf(aliasOf("s", types.String))),
expectedType: mapOf(aliasOf("s", types.String)),
expectedTypePfx: "",
expectedExprPfx: "*",
}, {
// **map[string]alias-of-string
in: ptrTo(ptrTo(mapOf(aliasOf("s", stringType)))),
expectedType: mapOf(aliasOf("s", stringType)),
in: ptrTo(ptrTo(mapOf(aliasOf("s", types.String)))),
expectedType: mapOf(aliasOf("s", types.String)),
expectedTypePfx: "",
expectedExprPfx: "**",
}, {
// ***map[string]alias-of-string
in: ptrTo(ptrTo(ptrTo(mapOf(aliasOf("s", stringType))))),
expectedType: mapOf(aliasOf("s", stringType)),
in: ptrTo(ptrTo(ptrTo(mapOf(aliasOf("s", types.String))))),
expectedType: mapOf(aliasOf("s", types.String)),
expectedTypePfx: "",
expectedExprPfx: "***",
}, {
// *alias-of-*string
in: ptrTo(aliasOf("ps", ptrTo(stringType))),
expectedType: aliasOf("ps", ptrTo(stringType)),
in: ptrTo(aliasOf("ps", ptrTo(types.String))),
expectedType: aliasOf("ps", ptrTo(types.String)),
expectedTypePfx: "",
expectedExprPfx: "*",
}, {
// **alias-of-*string
in: ptrTo(ptrTo(aliasOf("ps", ptrTo(stringType)))),
expectedType: aliasOf("ps", ptrTo(stringType)),
in: ptrTo(ptrTo(aliasOf("ps", ptrTo(types.String)))),
expectedType: aliasOf("ps", ptrTo(types.String)),
expectedTypePfx: "",
expectedExprPfx: "**",
}, {
// ***alias-of-*string
in: ptrTo(ptrTo(ptrTo(aliasOf("ps", ptrTo(stringType))))),
expectedType: aliasOf("ps", ptrTo(stringType)),
in: ptrTo(ptrTo(ptrTo(aliasOf("ps", ptrTo(types.String))))),
expectedType: aliasOf("ps", ptrTo(types.String)),
expectedTypePfx: "",
expectedExprPfx: "***",
}, {
// []alias-of-*string
in: sliceOf(aliasOf("ps", ptrTo(stringType))),
expectedType: sliceOf(aliasOf("ps", ptrTo(stringType))),
in: sliceOf(aliasOf("ps", ptrTo(types.String))),
expectedType: sliceOf(aliasOf("ps", ptrTo(types.String))),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// *[]alias-of-*string
in: ptrTo(sliceOf(aliasOf("ps", ptrTo(stringType)))),
expectedType: sliceOf(aliasOf("ps", ptrTo(stringType))),
in: ptrTo(sliceOf(aliasOf("ps", ptrTo(types.String)))),
expectedType: sliceOf(aliasOf("ps", ptrTo(types.String))),
expectedTypePfx: "",
expectedExprPfx: "*",
}, {
// **[]alias-of-*string
in: ptrTo(ptrTo(sliceOf(aliasOf("ps", ptrTo(stringType))))),
expectedType: sliceOf(aliasOf("ps", ptrTo(stringType))),
in: ptrTo(ptrTo(sliceOf(aliasOf("ps", ptrTo(types.String))))),
expectedType: sliceOf(aliasOf("ps", ptrTo(types.String))),
expectedTypePfx: "",
expectedExprPfx: "**",
}, {
// ***[]alias-of-*string
in: ptrTo(ptrTo(ptrTo(sliceOf(aliasOf("ps", ptrTo(stringType)))))),
expectedType: sliceOf(aliasOf("ps", ptrTo(stringType))),
in: ptrTo(ptrTo(ptrTo(sliceOf(aliasOf("ps", ptrTo(types.String)))))),
expectedType: sliceOf(aliasOf("ps", ptrTo(types.String))),
expectedTypePfx: "",
expectedExprPfx: "***",
}, {
// map[string]alias-of-*string
in: mapOf(aliasOf("ps", ptrTo(stringType))),
expectedType: mapOf(aliasOf("ps", ptrTo(stringType))),
in: mapOf(aliasOf("ps", ptrTo(types.String))),
expectedType: mapOf(aliasOf("ps", ptrTo(types.String))),
expectedTypePfx: "",
expectedExprPfx: "",
}, {
// *map[string]alias-of-*string
in: ptrTo(mapOf(aliasOf("ps", ptrTo(stringType)))),
expectedType: mapOf(aliasOf("ps", ptrTo(stringType))),
in: ptrTo(mapOf(aliasOf("ps", ptrTo(types.String)))),
expectedType: mapOf(aliasOf("ps", ptrTo(types.String))),
expectedTypePfx: "",
expectedExprPfx: "*",
}, {
// **map[string]alias-of-*string
in: ptrTo(ptrTo(mapOf(aliasOf("ps", ptrTo(stringType))))),
expectedType: mapOf(aliasOf("ps", ptrTo(stringType))),
in: ptrTo(ptrTo(mapOf(aliasOf("ps", ptrTo(types.String))))),
expectedType: mapOf(aliasOf("ps", ptrTo(types.String))),
expectedTypePfx: "",
expectedExprPfx: "**",
}, {
// ***map[string]alias-of-*string
in: ptrTo(ptrTo(ptrTo(mapOf(aliasOf("ps", ptrTo(stringType)))))),
expectedType: mapOf(aliasOf("ps", ptrTo(stringType))),
in: ptrTo(ptrTo(ptrTo(mapOf(aliasOf("ps", ptrTo(types.String)))))),
expectedType: mapOf(aliasOf("ps", ptrTo(types.String))),
expectedTypePfx: "",
expectedExprPfx: "***",
}}