diff --git a/build/tools.go b/build/tools.go index 12b785da2cf..aae735e5bfe 100644 --- a/build/tools.go +++ b/build/tools.go @@ -24,10 +24,10 @@ import ( // build script dependencies _ "github.com/onsi/ginkgo/v2/ginkgo" _ "k8s.io/code-generator/cmd/deepcopy-gen" + _ "k8s.io/code-generator/cmd/defaulter-gen" _ "k8s.io/code-generator/cmd/go-to-protobuf" _ "k8s.io/code-generator/cmd/go-to-protobuf/protoc-gen-gogo" _ "k8s.io/code-generator/cmd/import-boss" - _ "k8s.io/gengo/v2/examples/defaulter-gen/generators" _ "k8s.io/kube-openapi/cmd/openapi-gen" // submodule test dependencies diff --git a/staging/publishing/import-restrictions.yaml b/staging/publishing/import-restrictions.yaml index a846e5229f5..517af45de7e 100644 --- a/staging/publishing/import-restrictions.yaml +++ b/staging/publishing/import-restrictions.yaml @@ -51,6 +51,7 @@ - baseImportPath: "./staging/src/k8s.io/code-generator" ignoredSubTrees: - "./staging/src/k8s.io/code-generator/examples" + - "./staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests" allowedImports: - k8s.io/gengo - k8s.io/code-generator diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/args/args.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/args/args.go index de6221dc583..cac9e330808 100644 --- a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/args/args.go +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/args/args.go @@ -21,17 +21,20 @@ import ( "github.com/spf13/pflag" "k8s.io/gengo/v2/args" - "k8s.io/gengo/v2/examples/defaulter-gen/generators" ) // CustomArgs is used by the gengo framework to pass args specific to this generator. -type CustomArgs generators.CustomArgs +type CustomArgs struct { + OutputFile string + ExtraPeerDirs []string // Always consider these as last-ditch possibilities for conversions. + GoHeaderFile string +} // NewDefaults returns default arguments for the generator. func NewDefaults() (*args.GeneratorArgs, *CustomArgs) { genericArgs := args.Default() customArgs := &CustomArgs{} - genericArgs.CustomArgs = (*generators.CustomArgs)(customArgs) // convert to upstream type to make type-casts work there + genericArgs.CustomArgs = (*CustomArgs)(customArgs) // convert to upstream type to make type-casts work there return genericArgs, customArgs } @@ -47,7 +50,7 @@ func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) { // Validate checks the given arguments. func Validate(genericArgs *args.GeneratorArgs) error { - custom := genericArgs.CustomArgs.(*generators.CustomArgs) + custom := genericArgs.CustomArgs.(*CustomArgs) if len(custom.OutputFile) == 0 { return fmt.Errorf("--output-file must be specified") diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/generators/defaulter.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/generators/defaulter.go new file mode 100644 index 00000000000..e29b8f7357c --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/generators/defaulter.go @@ -0,0 +1,1241 @@ +/* +Copyright 2016 The Kubernetes Authors. + +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 generators + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + + defaulterargs "k8s.io/code-generator/cmd/defaulter-gen/args" + "k8s.io/gengo/v2/args" + "k8s.io/gengo/v2/generator" + "k8s.io/gengo/v2/namer" + "k8s.io/gengo/v2/types" + "k8s.io/klog/v2" +) + +var typeZeroValue = map[string]interface{}{ + "uint": 0., + "uint8": 0., + "uint16": 0., + "uint32": 0., + "uint64": 0., + "int": 0., + "int8": 0., + "int16": 0., + "int32": 0., + "int64": 0., + "byte": 0., + "float64": 0., + "float32": 0., + "bool": false, + "time.Time": "", + "string": "", + "integer": 0., + "number": 0., + "boolean": false, + "[]byte": "", // base64 encoded characters + "interface{}": interface{}(nil), +} + +// These are the comment tags that carry parameters for defaulter generation. +const tagName = "k8s:defaulter-gen" +const inputTagName = "k8s:defaulter-gen-input" +const defaultTagName = "default" + +func extractDefaultTag(comments []string) []string { + return types.ExtractCommentTags("+", comments)[defaultTagName] +} + +func extractTag(comments []string) []string { + return types.ExtractCommentTags("+", comments)[tagName] +} + +func extractInputTag(comments []string) []string { + return types.ExtractCommentTags("+", comments)[inputTagName] +} + +func checkTag(comments []string, require ...string) bool { + values := types.ExtractCommentTags("+", comments)[tagName] + if len(require) == 0 { + return len(values) == 1 && values[0] == "" + } + return reflect.DeepEqual(values, require) +} + +func defaultFnNamer() *namer.NameStrategy { + return &namer.NameStrategy{ + Prefix: "SetDefaults_", + Join: func(pre string, in []string, post string) string { + return pre + strings.Join(in, "_") + post + }, + } +} + +func objectDefaultFnNamer() *namer.NameStrategy { + return &namer.NameStrategy{ + Prefix: "SetObjectDefaults_", + Join: func(pre string, in []string, post string) string { + return pre + strings.Join(in, "_") + post + }, + } +} + +// NameSystems returns the name system used by the generators in this package. +func NameSystems() namer.NameSystems { + return namer.NameSystems{ + "public": namer.NewPublicNamer(1), + "raw": namer.NewRawNamer("", nil), + "defaultfn": defaultFnNamer(), + "objectdefaultfn": objectDefaultFnNamer(), + } +} + +// DefaultNameSystem returns the default name system for ordering the types to be +// processed by the generators in this package. +func DefaultNameSystem() string { + return "public" +} + +// defaults holds the declared defaulting functions for a given type (all defaulting functions +// are expected to be func(1)) +type defaults struct { + // object is the defaulter function for a top level type (typically one with TypeMeta) that + // invokes all child defaulters. May be nil if the object defaulter has not yet been generated. + object *types.Type + // base is a defaulter function defined for a type SetDefaults_Pod which does not invoke all + // child defaults - the base defaulter alone is insufficient to default a type + base *types.Type + // additional is zero or more defaulter functions of the form SetDefaults_Pod_XXXX that can be + // included in the Object defaulter. + additional []*types.Type +} + +// All of the types in conversions map are of type "DeclarationOf" with +// the underlying type being "Func". +type defaulterFuncMap map[*types.Type]defaults + +// Returns all manually-defined defaulting functions in the package. +func getManualDefaultingFunctions(context *generator.Context, pkg *types.Package, manualMap defaulterFuncMap) { + buffer := &bytes.Buffer{} + sw := generator.NewSnippetWriter(buffer, context, "$", "$") + + for _, f := range pkg.Functions { + if f.Underlying == nil || f.Underlying.Kind != types.Func { + klog.Errorf("Malformed function: %#v", f) + continue + } + if f.Underlying.Signature == nil { + klog.Errorf("Function without signature: %#v", f) + continue + } + signature := f.Underlying.Signature + // Check whether the function is defaulting function. + // Note that all of them have signature: + // object: func SetObjectDefaults_inType(*inType) + // base: func SetDefaults_inType(*inType) + // additional: func SetDefaults_inType_Qualifier(*inType) + if signature.Receiver != nil { + continue + } + if len(signature.Parameters) != 1 { + continue + } + if len(signature.Results) != 0 { + continue + } + inType := signature.Parameters[0] + if inType.Kind != types.Pointer { + continue + } + // Check if this is the primary defaulter. + args := defaultingArgsFromType(inType.Elem) + sw.Do("$.inType|defaultfn$", args) + switch { + case f.Name.Name == buffer.String(): + key := inType.Elem + // We might scan the same package twice, and that's OK. + v, ok := manualMap[key] + if ok && v.base != nil && v.base.Name.Package != pkg.Path { + panic(fmt.Sprintf("duplicate static defaulter defined: %#v", key)) + } + v.base = f + manualMap[key] = v + klog.V(6).Infof("found base defaulter function for %s from %s", key.Name, f.Name) + // Is one of the additional defaulters - a top level defaulter on a type that is + // also invoked. + case strings.HasPrefix(f.Name.Name, buffer.String()+"_"): + key := inType.Elem + v, ok := manualMap[key] + if ok { + exists := false + for _, existing := range v.additional { + if existing.Name == f.Name { + exists = true + break + } + } + if exists { + continue + } + } + v.additional = append(v.additional, f) + manualMap[key] = v + klog.V(6).Infof("found additional defaulter function for %s from %s", key.Name, f.Name) + } + buffer.Reset() + sw.Do("$.inType|objectdefaultfn$", args) + if f.Name.Name == buffer.String() { + key := inType.Elem + // We might scan the same package twice, and that's OK. + v, ok := manualMap[key] + if ok && v.base != nil && v.base.Name.Package != pkg.Path { + panic(fmt.Sprintf("duplicate static defaulter defined: %#v", key)) + } + v.object = f + manualMap[key] = v + klog.V(6).Infof("found object defaulter function for %s from %s", key.Name, f.Name) + } + buffer.Reset() + } +} + +func GetTargets(context *generator.Context, arguments *args.GeneratorArgs) []generator.Target { + customArgs := arguments.CustomArgs.(*defaulterargs.CustomArgs) + + boilerplate, err := args.GoBoilerplate(customArgs.GoHeaderFile, args.StdBuildTag, args.StdGeneratedBy) + if err != nil { + klog.Fatalf("Failed loading boilerplate: %v", err) + } + + targets := []generator.Target{} + + // Accumulate pre-existing default functions. + // TODO: This is too ad-hoc. We need a better way. + existingDefaulters := defaulterFuncMap{} + + buffer := &bytes.Buffer{} + sw := generator.NewSnippetWriter(buffer, context, "$", "$") + + // First load other "input" packages. We do this as a single call because + // it is MUCH faster. + inputPkgs := make([]string, 0, len(context.Inputs)) + pkgToInput := map[string]string{} + for _, i := range context.Inputs { + klog.V(5).Infof("considering pkg %q", i) + + pkg := context.Universe[i] + if pkg == nil { + // If the input had no Go files, for example. + continue + } + + // if the types are not in the same package where the defaulter functions to be generated + inputTags := extractInputTag(pkg.Comments) + if len(inputTags) > 1 { + panic(fmt.Sprintf("there may only be one input tag, got %#v", inputTags)) + } + if len(inputTags) == 1 { + inputPath := inputTags[0] + if strings.HasPrefix(inputPath, "./") || strings.HasPrefix(inputPath, "../") { + // this is a relative dir, which will not work under gomodules. + // join with the local package path, but warn + klog.Warningf("relative path %s=%s will not work under gomodule mode; use full package path (as used by 'import') instead", inputTagName, inputPath) + inputPath = filepath.Join(pkg.Path, inputTags[0]) + } + + klog.V(5).Infof(" input pkg %v", inputPath) + inputPkgs = append(inputPkgs, inputPath) + pkgToInput[i] = inputPath + } else { + pkgToInput[i] = i + } + } + + // Make sure explicit peer-packages are added. + var peerPkgs []string + for _, pkg := range customArgs.ExtraPeerDirs { + // In case someone specifies a peer as a path into vendor, convert + // it to its "real" package path. + if i := strings.Index(pkg, "/vendor/"); i != -1 { + pkg = pkg[i+len("/vendor/"):] + } + peerPkgs = append(peerPkgs, pkg) + } + if expanded, err := context.FindPackages(peerPkgs...); err != nil { + klog.Fatalf("cannot find peer packages: %v", err) + } else { + peerPkgs = expanded // now in fully canonical form + } + inputPkgs = append(inputPkgs, peerPkgs...) + + if len(inputPkgs) > 0 { + if _, err := context.LoadPackages(inputPkgs...); err != nil { + klog.Fatalf("cannot load packages: %v", err) + } + } + // update context.Order to the latest context.Universe + orderer := namer.Orderer{Namer: namer.NewPublicNamer(1)} + context.Order = orderer.OrderUniverse(context.Universe) + + for _, i := range context.Inputs { + pkg := context.Universe[i] + if pkg == nil { + // If the input had no Go files, for example. + continue + } + + // typesPkg is where the types that needs defaulter are defined. + // Sometimes it is different from pkg. For example, kubernetes core/v1 + // types are defined in k8s.io/api/core/v1, while the pkg which holds + // defaulter code is at k/k/pkg/api/v1. + typesPkg := pkg + + // Add defaulting functions. + getManualDefaultingFunctions(context, pkg, existingDefaulters) + + // Also look for defaulting functions in peer-packages. + for _, pp := range peerPkgs { + getManualDefaultingFunctions(context, context.Universe[pp], existingDefaulters) + } + + typesWith := extractTag(pkg.Comments) + shouldCreateObjectDefaulterFn := func(t *types.Type) bool { + if defaults, ok := existingDefaulters[t]; ok && defaults.object != nil { + // A default generator is defined + baseTypeName := "" + if defaults.base != nil { + baseTypeName = defaults.base.Name.String() + } + klog.V(5).Infof(" an object defaulter already exists as %s", baseTypeName) + return false + } + // opt-out + if checkTag(t.SecondClosestCommentLines, "false") { + return false + } + // opt-in + if checkTag(t.SecondClosestCommentLines, "true") { + return true + } + // For every k8s:defaulter-gen tag at the package level, interpret the value as a + // field name (like TypeMeta, ListMeta, ObjectMeta) and trigger defaulter generation + // for any type with any of the matching field names. Provides a more useful package + // level defaulting than global (because we only need defaulters on a subset of objects - + // usually those with TypeMeta). + if t.Kind == types.Struct && len(typesWith) > 0 { + for _, field := range t.Members { + for _, s := range typesWith { + if field.Name == s { + return true + } + } + } + } + return false + } + + // Find the right input pkg, which might not be this one. + inputPath := pkgToInput[i] + typesPkg = context.Universe[inputPath] + + newDefaulters := defaulterFuncMap{} + for _, t := range typesPkg.Types { + if !shouldCreateObjectDefaulterFn(t) { + continue + } + if namer.IsPrivateGoName(t.Name.Name) { + // We won't be able to convert to a private type. + klog.V(5).Infof(" found a type %v, but it is a private name", t) + continue + } + + // create a synthetic type we can use during generation + newDefaulters[t] = defaults{} + } + + // only generate defaulters for objects that actually have defined defaulters + // prevents empty defaulters from being registered + for { + promoted := 0 + for t, d := range newDefaulters { + if d.object != nil { + continue + } + if newCallTreeForType(existingDefaulters, newDefaulters).build(t, true) != nil { + args := defaultingArgsFromType(t) + sw.Do("$.inType|objectdefaultfn$", args) + newDefaulters[t] = defaults{ + object: &types.Type{ + Name: types.Name{ + Package: pkg.Path, + Name: buffer.String(), + }, + Kind: types.Func, + }, + } + buffer.Reset() + promoted++ + } + } + if promoted != 0 { + continue + } + + // prune any types that were not used + for t, d := range newDefaulters { + if d.object == nil { + klog.V(6).Infof("did not generate defaulter for %s because no child defaulters were registered", t.Name) + delete(newDefaulters, t) + } + } + break + } + + if len(newDefaulters) == 0 { + klog.V(5).Infof("no defaulters in package %s", pkg.Name) + } + + targets = append(targets, + &generator.SimpleTarget{ + PkgName: filepath.Base(pkg.Path), + PkgPath: pkg.Path, + PkgDir: pkg.SourcePath, // output pkg is the same as the input + HeaderComment: boilerplate, + + FilterFunc: func(c *generator.Context, t *types.Type) bool { + return t.Name.Package == typesPkg.Path + }, + + GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) { + return []generator.Generator{ + NewGenDefaulter(customArgs.OutputFile, typesPkg.Path, pkg.Path, existingDefaulters, newDefaulters, peerPkgs), + } + }, + }) + } + return targets +} + +// callTreeForType contains fields necessary to build a tree for types. +type callTreeForType struct { + existingDefaulters defaulterFuncMap + newDefaulters defaulterFuncMap + currentlyBuildingTypes map[*types.Type]bool +} + +func newCallTreeForType(existingDefaulters, newDefaulters defaulterFuncMap) *callTreeForType { + return &callTreeForType{ + existingDefaulters: existingDefaulters, + newDefaulters: newDefaulters, + currentlyBuildingTypes: make(map[*types.Type]bool), + } +} + +// resolveType follows pointers and aliases of `t` until reaching the first +// non-pointer type in `t's` herarchy +func resolveTypeAndDepth(t *types.Type) (*types.Type, int) { + var prev *types.Type + depth := 0 + for prev != t { + prev = t + if t.Kind == types.Alias { + t = t.Underlying + } else if t.Kind == types.Pointer { + t = t.Elem + depth += 1 + } + } + return t, depth +} + +// getPointerElementPath follows pointers and aliases to returns all +// pointer elements in the path from the given type, to its base value type. +// +// Example: +// +// type MyString string +// type MyStringPointer *MyString +// type MyStringPointerPointer *MyStringPointer +// type MyStringAlias MyStringPointer +// type MyStringAliasPointer *MyStringAlias +// type MyStringAliasDoublePointer **MyStringAlias +// +// t | defaultPointerElementPath(t) +// ---------------------------|---------------------------------------- +// MyString | [] +// MyStringPointer | [MyString] +// MyStringPointerPointer | [MyStringPointer, MyString] +// MyStringAlias | [MyStringPointer, MyString] +// MyStringAliasPointer | [MyStringAlias, MyStringPointer, MyString] +// MyStringAliasDoublePointer | [*MyStringAlias, MyStringAlias, MyStringPointer, MyString] +func getPointerElementPath(t *types.Type) []*types.Type { + var path []*types.Type + for t != nil { + switch t.Kind { + case types.Alias: + t = t.Underlying + case types.Pointer: + t = t.Elem + path = append(path, t) + default: + t = nil + } + } + return path +} + +// getNestedDefault returns the first default value when resolving alias types +func getNestedDefault(t *types.Type) string { + var prev *types.Type + for prev != t { + prev = t + defaultMap := extractDefaultTag(t.CommentLines) + if len(defaultMap) == 1 && defaultMap[0] != "" { + return defaultMap[0] + } + if t.Kind == types.Alias { + t = t.Underlying + } else if t.Kind == types.Pointer { + t = t.Elem + } + } + return "" +} + +var refRE = regexp.MustCompile(`^ref\((?P[^"]+)\)$`) +var refREIdentIndex = refRE.SubexpIndex("reference") + +// parseSymbolReference looks for strings that match one of the following: +// - ref(Ident) +// - ref(pkgpath.Ident) +// If the input string matches either of these, it will return the (optional) +// pkgpath, the Ident, and true. Otherwise it will return empty strings and +// false. +func parseSymbolReference(s, sourcePackage string) (types.Name, bool) { + matches := refRE.FindStringSubmatch(s) + if len(matches) < refREIdentIndex || matches[refREIdentIndex] == "" { + return types.Name{}, false + } + + contents := matches[refREIdentIndex] + name := types.ParseFullyQualifiedName(contents) + if len(name.Package) == 0 { + name.Package = sourcePackage + } + return name, true +} + +func populateDefaultValue(node *callNode, t *types.Type, tags string, commentLines []string, commentPackage string) *callNode { + defaultMap := extractDefaultTag(commentLines) + var defaultString string + if len(defaultMap) == 1 { + defaultString = defaultMap[0] + } else if len(defaultMap) > 1 { + klog.Fatalf("Found more than one default tag for %v", t.Kind) + } + + baseT, depth := resolveTypeAndDepth(t) + if depth > 0 && defaultString == "" { + defaultString = getNestedDefault(t) + } + + if len(defaultString) == 0 { + return node + } + var symbolReference types.Name + var defaultValue interface{} + if id, ok := parseSymbolReference(defaultString, commentPackage); ok { + symbolReference = id + defaultString = "" + } else if err := json.Unmarshal([]byte(defaultString), &defaultValue); err != nil { + klog.Fatalf("Failed to unmarshal default: %v", err) + } + + if defaultValue != nil { + zero := typeZeroValue[t.String()] + if reflect.DeepEqual(defaultValue, zero) { + // If the default value annotation matches the default value for the type, + // do not generate any defaulting function + return node + } + } + + // callNodes are not automatically generated for primitive types. Generate one if the callNode does not exist + if node == nil { + node = &callNode{} + node.markerOnly = true + } + + node.defaultIsPrimitive = baseT.IsPrimitive() + node.defaultType = baseT + node.defaultTopLevelType = t + node.defaultValue.InlineConstant = defaultString + node.defaultValue.SymbolReference = symbolReference + return node +} + +// build creates a tree of paths to fields (based on how they would be accessed in Go - pointer, elem, +// slice, or key) and the functions that should be invoked on each field. An in-order traversal of the resulting tree +// can be used to generate a Go function that invokes each nested function on the appropriate type. The return +// value may be nil if there are no functions to call on type or the type is a primitive (Defaulters can only be +// invoked on structs today). When root is true this function will not use a newDefaulter. existingDefaulters should +// contain all defaulting functions by type defined in code - newDefaulters should contain all object defaulters +// that could be or will be generated. If newDefaulters has an entry for a type, but the 'object' field is nil, +// this function skips adding that defaulter - this allows us to avoid generating object defaulter functions for +// list types that call empty defaulters. +func (c *callTreeForType) build(t *types.Type, root bool) *callNode { + parent := &callNode{} + + if root { + // the root node is always a pointer + parent.elem = true + } + + defaults := c.existingDefaulters[t] + newDefaults, generated := c.newDefaulters[t] + switch { + case !root && generated && newDefaults.object != nil: + parent.call = append(parent.call, newDefaults.object) + // if we will be generating the defaulter, it by definition is a covering + // defaulter, so we halt recursion + klog.V(6).Infof("the defaulter %s will be generated as an object defaulter", t.Name) + return parent + + case defaults.object != nil: + // object defaulters are always covering + parent.call = append(parent.call, defaults.object) + return parent + + case defaults.base != nil: + parent.call = append(parent.call, defaults.base) + // if the base function indicates it "covers" (it already includes defaulters) + // we can halt recursion + if checkTag(defaults.base.CommentLines, "covers") { + klog.V(6).Infof("the defaulter %s indicates it covers all sub generators", t.Name) + return parent + } + } + + // base has been added already, now add any additional defaulters defined for this object + parent.call = append(parent.call, defaults.additional...) + + // if the type already exists, don't build the tree for it and don't generate anything. + // This is used to avoid recursion for nested recursive types. + if c.currentlyBuildingTypes[t] { + return nil + } + // if type doesn't exist, mark it as existing + c.currentlyBuildingTypes[t] = true + + defer func() { + // The type will now acts as a parent, not a nested recursive type. + // We can now build the tree for it safely. + c.currentlyBuildingTypes[t] = false + }() + + switch t.Kind { + case types.Pointer: + if child := c.build(t.Elem, false); child != nil { + child.elem = true + parent.children = append(parent.children, *child) + } + case types.Slice, types.Array: + if child := c.build(t.Elem, false); child != nil { + child.index = true + if t.Elem.Kind == types.Pointer { + child.elem = true + } + parent.children = append(parent.children, *child) + } else if member := populateDefaultValue(nil, t.Elem, "", t.Elem.CommentLines, t.Elem.Name.Package); member != nil { + member.index = true + parent.children = append(parent.children, *member) + } + case types.Map: + if child := c.build(t.Elem, false); child != nil { + child.key = true + parent.children = append(parent.children, *child) + } else if member := populateDefaultValue(nil, t.Elem, "", t.Elem.CommentLines, t.Elem.Name.Package); member != nil { + member.key = true + parent.children = append(parent.children, *member) + } + + case types.Struct: + for _, field := range t.Members { + name := field.Name + if len(name) == 0 { + if field.Type.Kind == types.Pointer { + name = field.Type.Elem.Name.Name + } else { + name = field.Type.Name.Name + } + } + if child := c.build(field.Type, false); child != nil { + child.field = name + populateDefaultValue(child, field.Type, field.Tags, field.CommentLines, field.Type.Name.Package) + parent.children = append(parent.children, *child) + } else if member := populateDefaultValue(nil, field.Type, field.Tags, field.CommentLines, t.Name.Package); member != nil { + member.field = name + parent.children = append(parent.children, *member) + } + } + case types.Alias: + if child := c.build(t.Underlying, false); child != nil { + parent.children = append(parent.children, *child) + } + } + if len(parent.children) == 0 && len(parent.call) == 0 { + // klog.V(6).Infof("decided type %s needs no generation", t.Name) + return nil + } + return parent +} + +const ( + runtimePackagePath = "k8s.io/apimachinery/pkg/runtime" + conversionPackagePath = "k8s.io/apimachinery/pkg/conversion" +) + +// genDefaulter produces a file with a autogenerated conversions. +type genDefaulter struct { + generator.GoGenerator + typesPackage string + outputPackage string + peerPackages []string + newDefaulters defaulterFuncMap + existingDefaulters defaulterFuncMap + imports namer.ImportTracker + typesForInit []*types.Type +} + +func NewGenDefaulter(outputFilename, typesPackage, outputPackage string, existingDefaulters, newDefaulters defaulterFuncMap, peerPkgs []string) generator.Generator { + return &genDefaulter{ + GoGenerator: generator.GoGenerator{ + OutputFilename: outputFilename, + }, + typesPackage: typesPackage, + outputPackage: outputPackage, + peerPackages: peerPkgs, + newDefaulters: newDefaulters, + existingDefaulters: existingDefaulters, + imports: generator.NewImportTrackerForPackage(outputPackage), + typesForInit: make([]*types.Type, 0), + } +} + +func (g *genDefaulter) Namers(c *generator.Context) namer.NameSystems { + // Have the raw namer for this file track what it imports. + return namer.NameSystems{ + "raw": namer.NewRawNamer(g.outputPackage, g.imports), + } +} + +func (g *genDefaulter) isOtherPackage(pkg string) bool { + if pkg == g.outputPackage { + return false + } + if strings.HasSuffix(pkg, `"`+g.outputPackage+`"`) { + return false + } + return true +} + +func (g *genDefaulter) Filter(c *generator.Context, t *types.Type) bool { + defaults, ok := g.newDefaulters[t] + if !ok || defaults.object == nil { + return false + } + g.typesForInit = append(g.typesForInit, t) + return true +} + +func (g *genDefaulter) Imports(c *generator.Context) (imports []string) { + var importLines []string + for _, singleImport := range g.imports.ImportLines() { + if g.isOtherPackage(singleImport) { + importLines = append(importLines, singleImport) + } + } + return importLines +} + +func (g *genDefaulter) Init(c *generator.Context, w io.Writer) error { + sw := generator.NewSnippetWriter(w, c, "$", "$") + + scheme := c.Universe.Type(types.Name{Package: runtimePackagePath, Name: "Scheme"}) + schemePtr := &types.Type{ + Kind: types.Pointer, + Elem: scheme, + } + sw.Do("// RegisterDefaults adds defaulters functions to the given scheme.\n", nil) + sw.Do("// Public to allow building arbitrary schemes.\n", nil) + sw.Do("// All generated defaulters are covering - they call all nested defaulters.\n", nil) + sw.Do("func RegisterDefaults(scheme $.|raw$) error {\n", schemePtr) + for _, t := range g.typesForInit { + args := defaultingArgsFromType(t) + sw.Do("scheme.AddTypeDefaultingFunc(&$.inType|raw${}, func(obj interface{}) { $.inType|objectdefaultfn$(obj.(*$.inType|raw$)) })\n", args) + } + sw.Do("return nil\n", nil) + sw.Do("}\n\n", nil) + return sw.Error() +} + +func (g *genDefaulter) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { + if _, ok := g.newDefaulters[t]; !ok { + return nil + } + + klog.V(5).Infof("generating for type %v", t) + + callTree := newCallTreeForType(g.existingDefaulters, g.newDefaulters).build(t, true) + if callTree == nil { + klog.V(5).Infof(" no defaulters defined") + return nil + } + i := 0 + callTree.VisitInOrder(func(ancestors []*callNode, current *callNode) { + if ref := ¤t.defaultValue.SymbolReference; len(ref.Name) > 0 { + // Ensure package for symbol is imported in output generation + g.imports.AddSymbol(*ref) + + // Rewrite the fully qualified name using the local package name + // from the imports + ref.Package = g.imports.LocalNameOf(ref.Package) + } + + if len(current.call) == 0 { + return + } + path := callPath(append(ancestors, current)) + klog.V(5).Infof(" %d: %s", i, path) + i++ + }) + + sw := generator.NewSnippetWriter(w, c, "$", "$") + g.generateDefaulter(t, callTree, sw) + return sw.Error() +} + +func defaultingArgsFromType(inType *types.Type) generator.Args { + return generator.Args{ + "inType": inType, + } +} + +func (g *genDefaulter) generateDefaulter(inType *types.Type, callTree *callNode, sw *generator.SnippetWriter) { + sw.Do("func $.inType|objectdefaultfn$(in *$.inType|raw$) {\n", defaultingArgsFromType(inType)) + callTree.WriteMethod("in", 0, nil, sw) + sw.Do("}\n\n", nil) +} + +// callNode represents an entry in a tree of Go type accessors - the path from the root to a leaf represents +// how in Go code an access would be performed. For example, if a defaulting function exists on a container +// lifecycle hook, to invoke that defaulter correctly would require this Go code: +// +// for i := range pod.Spec.Containers { +// o := &pod.Spec.Containers[i] +// if o.LifecycleHook != nil { +// SetDefaults_LifecycleHook(o.LifecycleHook) +// } +// } +// +// That would be represented by a call tree like: +// +// callNode +// field: "Spec" +// children: +// - field: "Containers" +// children: +// - index: true +// children: +// - field: "LifecycleHook" +// elem: true +// call: +// - SetDefaults_LifecycleHook +// +// which we can traverse to build that Go struct (you must call the field Spec, then Containers, then range over +// that field, then check whether the LifecycleHook field is nil, before calling SetDefaults_LifecycleHook on +// the pointer to that field). +type callNode struct { + // field is the name of the Go member to access + field string + // key is true if this is a map and we must range over the key and values + key bool + // index is true if this is a slice and we must range over the slice values + index bool + // elem is true if the previous elements refer to a pointer (typically just field) + elem bool + + // call is all of the functions that must be invoked on this particular node, in order + call []*types.Type + // children is the child call nodes that must also be traversed + children []callNode + + // defaultValue is the defaultValue of a callNode struct + // Only primitive types and pointer types are eligible to have a default value + defaultValue defaultValue + + // defaultIsPrimitive is used to determine how to assign the default value. + // Primitive types will be directly assigned while complex types will use JSON unmarshalling + defaultIsPrimitive bool + + // markerOnly is true if the callNode exists solely to fill in a default value + markerOnly bool + + // defaultType is the transitive underlying/element type of the node. + // The provided default value literal or reference is expected to be + // convertible to this type. + // + // e.g: + // node type = *string -> defaultType = string + // node type = StringPointerAlias -> defaultType = string + // Only populated if defaultIsPrimitive is true + defaultType *types.Type + + // defaultTopLevelType is the final type the value should resolve to + // This is in constrast with default type, which resolves aliases and pointers. + defaultTopLevelType *types.Type +} + +type defaultValue struct { + // The value was written directly in the marker comment and + // has been parsed as JSON + InlineConstant string + // The name of the symbol relative to the parsed package path + // i.e. k8s.io/pkg.apis.v1.Foo if from another package or simply `Foo` + // if within the same package. + SymbolReference types.Name +} + +func (d defaultValue) IsEmpty() bool { + resolved := d.Resolved() + return resolved == "" +} + +func (d defaultValue) Resolved() string { + if len(d.InlineConstant) > 0 { + return d.InlineConstant + } + return d.SymbolReference.String() +} + +// CallNodeVisitorFunc is a function for visiting a call tree. ancestors is the list of all parents +// of this node to the root of the tree - will be empty at the root. +type CallNodeVisitorFunc func(ancestors []*callNode, node *callNode) + +func (n *callNode) VisitInOrder(fn CallNodeVisitorFunc) { + n.visitInOrder(nil, fn) +} + +func (n *callNode) visitInOrder(ancestors []*callNode, fn CallNodeVisitorFunc) { + fn(ancestors, n) + ancestors = append(ancestors, n) + for i := range n.children { + n.children[i].visitInOrder(ancestors, fn) + } +} + +var ( + indexVariables = "ijklmnop" + localVariables = "abcdefgh" +) + +// varsForDepth creates temporary variables guaranteed to be unique within lexical Go scopes +// of this depth in a function. It uses canonical Go loop variables for the first 7 levels +// and then resorts to uglier prefixes. +func varsForDepth(depth int) (index, local string) { + if depth > len(indexVariables) { + index = fmt.Sprintf("i%d", depth) + } else { + index = indexVariables[depth : depth+1] + } + if depth > len(localVariables) { + local = fmt.Sprintf("local%d", depth) + } else { + local = localVariables[depth : depth+1] + } + return +} + +// writeCalls generates a list of function calls based on the calls field for the provided variable +// name and pointer. +func (n *callNode) writeCalls(varName string, isVarPointer bool, sw *generator.SnippetWriter) { + accessor := varName + if !isVarPointer { + accessor = "&" + accessor + } + for _, fn := range n.call { + sw.Do("$.fn|raw$($.var$)\n", generator.Args{ + "fn": fn, + "var": accessor, + }) + } +} + +func getTypeZeroValue(t string) (interface{}, error) { + defaultZero, ok := typeZeroValue[t] + if !ok { + return nil, fmt.Errorf("cannot find zero value for type %v in typeZeroValue", t) + } + + // To generate the code for empty string, they must be quoted + if defaultZero == "" { + defaultZero = strconv.Quote(defaultZero.(string)) + } + return defaultZero, nil +} + +func (n *callNode) writeDefaulter(varName string, index string, isVarPointer bool, sw *generator.SnippetWriter) { + if n.defaultValue.IsEmpty() { + return + } + args := generator.Args{ + "defaultValue": n.defaultValue.Resolved(), + "varName": varName, + "index": index, + "varTopType": n.defaultTopLevelType, + } + + variablePlaceholder := "" + + if n.index { + // Defaulting for array + variablePlaceholder = "$.varName$[$.index$]" + } else if n.key { + // Defaulting for map + variablePlaceholder = "$.varName$[$.index$]" + mapDefaultVar := args["index"].(string) + "_default" + args["mapDefaultVar"] = mapDefaultVar + } else { + // Defaulting for primitive type + variablePlaceholder = "$.varName$" + } + + // defaultIsPrimitive is true if the type or underlying type (in an array/map) is primitive + // or is a pointer to a primitive type + // (Eg: int, map[string]*string, []int) + if n.defaultIsPrimitive { + // If the default value is a primitive when the assigned type is a pointer + // keep using the address-of operator on the primitive value until the types match + if pointerPath := getPointerElementPath(n.defaultTopLevelType); len(pointerPath) > 0 { + // If the destination is a pointer, the last element in + // defaultDepth is the element type of the bottommost pointer: + // the base type of our default value. + destElemType := pointerPath[len(pointerPath)-1] + pointerArgs := args.WithArgs(generator.Args{ + "varDepth": len(pointerPath), + "baseElemType": destElemType, + }) + + sw.Do(fmt.Sprintf("if %s == nil {\n", variablePlaceholder), pointerArgs) + if len(n.defaultValue.InlineConstant) > 0 { + // If default value is a literal then it can be assigned via var stmt + sw.Do("var ptrVar$.varDepth$ $.baseElemType|raw$ = $.defaultValue$\n", pointerArgs) + } else { + // If default value is not a literal then it may need to be casted + // to the base type of the destination pointer + sw.Do("ptrVar$.varDepth$ := $.baseElemType|raw$($.defaultValue$)\n", pointerArgs) + } + + for i := len(pointerPath); i >= 1; i-- { + dest := fmt.Sprintf("ptrVar%d", i-1) + assignment := ":=" + if i == 1 { + // Last assignment is into the storage destination + dest = variablePlaceholder + assignment = "=" + } + + sourceType := "*" + destElemType.String() + if i == len(pointerPath) { + // Initial value is not a pointer + sourceType = destElemType.String() + } + destElemType = pointerPath[i-1] + + // Cannot include `dest` into args since its value may be + // `variablePlaceholder` which is a template, not a value + elementArgs := pointerArgs.WithArgs(generator.Args{ + "assignment": assignment, + "source": fmt.Sprintf("ptrVar%d", i), + "destElemType": destElemType, + }) + + // Skip cast if type is exact match + if destElemType.String() == sourceType { + sw.Do(fmt.Sprintf("%v $.assignment$ &$.source$\n", dest), elementArgs) + } else { + sw.Do(fmt.Sprintf("%v $.assignment$ (*$.destElemType|raw$)(&$.source$)\n", dest), elementArgs) + } + } + } else { + // For primitive types, nil checks cannot be used and the zero value must be determined + defaultZero, err := getTypeZeroValue(n.defaultType.String()) + if err != nil { + klog.Error(err) + } + args["defaultZero"] = defaultZero + + sw.Do(fmt.Sprintf("if %s == $.defaultZero$ {\n", variablePlaceholder), args) + + if len(n.defaultValue.InlineConstant) > 0 { + sw.Do(fmt.Sprintf("%s = $.defaultValue$", variablePlaceholder), args) + } else { + sw.Do(fmt.Sprintf("%s = $.varTopType|raw$($.defaultValue$)", variablePlaceholder), args) + } + } + } else { + sw.Do(fmt.Sprintf("if %s == nil {\n", variablePlaceholder), args) + // Map values are not directly addressable and we need a temporary variable to do json unmarshalling + // This applies to maps with non-primitive values (eg: map[string]SubStruct) + if n.key { + sw.Do("$.mapDefaultVar$ := $.varName$[$.index$]\n", args) + sw.Do("if err := json.Unmarshal([]byte(`$.defaultValue$`), &$.mapDefaultVar$); err != nil {\n", args) + } else { + variablePointer := variablePlaceholder + if !isVarPointer { + variablePointer = "&" + variablePointer + } + sw.Do(fmt.Sprintf("if err := json.Unmarshal([]byte(`$.defaultValue$`), %s); err != nil {\n", variablePointer), args) + } + sw.Do("panic(err)\n", nil) + sw.Do("}\n", nil) + if n.key { + sw.Do("$.varName$[$.index$] = $.mapDefaultVar$\n", args) + } + } + sw.Do("}\n", nil) +} + +// WriteMethod performs an in-order traversal of the calltree, generating loops and if blocks as necessary +// to correctly turn the call tree into a method body that invokes all calls on all child nodes of the call tree. +// Depth is used to generate local variables at the proper depth. +func (n *callNode) WriteMethod(varName string, depth int, ancestors []*callNode, sw *generator.SnippetWriter) { + // if len(n.call) > 0 { + // sw.Do(fmt.Sprintf("// %s\n", callPath(append(ancestors, n)).String()), nil) + // } + + if len(n.field) > 0 { + varName = varName + "." + n.field + } + + index, local := varsForDepth(depth) + vars := generator.Args{ + "index": index, + "local": local, + "var": varName, + } + + isPointer := n.elem && !n.index + if isPointer && len(ancestors) > 0 { + sw.Do("if $.var$ != nil {\n", vars) + } + + switch { + case n.index: + sw.Do("for $.index$ := range $.var$ {\n", vars) + if !n.markerOnly { + if n.elem { + sw.Do("$.local$ := $.var$[$.index$]\n", vars) + } else { + sw.Do("$.local$ := &$.var$[$.index$]\n", vars) + } + } + + n.writeDefaulter(varName, index, isPointer, sw) + n.writeCalls(local, true, sw) + for i := range n.children { + n.children[i].WriteMethod(local, depth+1, append(ancestors, n), sw) + } + sw.Do("}\n", nil) + case n.key: + if !n.defaultValue.IsEmpty() { + // Map keys are typed and cannot share the same index variable as arrays and other maps + index = index + "_" + ancestors[len(ancestors)-1].field + vars["index"] = index + sw.Do("for $.index$ := range $.var$ {\n", vars) + n.writeDefaulter(varName, index, isPointer, sw) + sw.Do("}\n", nil) + } + default: + n.writeDefaulter(varName, index, isPointer, sw) + n.writeCalls(varName, isPointer, sw) + for i := range n.children { + n.children[i].WriteMethod(varName, depth, append(ancestors, n), sw) + } + } + + if isPointer && len(ancestors) > 0 { + sw.Do("}\n", nil) + } +} + +type callPath []*callNode + +// String prints a representation of a callPath that roughly approximates what a Go accessor +// would look like. Used for debugging only. +func (path callPath) String() string { + if len(path) == 0 { + return "" + } + var parts []string + for _, p := range path { + last := len(parts) - 1 + switch { + case p.elem: + if len(parts) > 0 { + parts[last] = "*" + parts[last] + } else { + parts = append(parts, "*") + } + case p.index: + if len(parts) > 0 { + parts[last] = parts[last] + "[i]" + } else { + parts = append(parts, "[i]") + } + case p.key: + if len(parts) > 0 { + parts[last] = parts[last] + "[key]" + } else { + parts = append(parts, "[key]") + } + default: + if len(p.field) > 0 { + parts = append(parts, p.field) + } else { + parts = append(parts, "") + } + } + } + var calls []string + for _, fn := range path[len(path)-1].call { + calls = append(calls, fn.Name.String()) + } + if len(calls) == 0 { + calls = append(calls, "") + } + + return strings.Join(parts, ".") + " calls " + strings.Join(calls, ", ") +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/main.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/main.go index 0dc11e416cd..02425d41a60 100644 --- a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/main.go +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/main.go @@ -46,8 +46,8 @@ import ( "github.com/spf13/pflag" generatorargs "k8s.io/code-generator/cmd/defaulter-gen/args" + "k8s.io/code-generator/cmd/defaulter-gen/generators" "k8s.io/gengo/v2/args" - "k8s.io/gengo/v2/examples/defaulter-gen/generators" "k8s.io/klog/v2" ) diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty/doc.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty/doc.go new file mode 100644 index 00000000000..5dc6bba16f5 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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. +*/ + +// +k8s:defaulter-gen=covers + +// This is a test package. +package empty // import "k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty" diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty/type.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty/type.go new file mode 100644 index 00000000000..707e6fa4a62 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty/type.go @@ -0,0 +1,29 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 empty + +// Only test +type Ttest struct { + BoolField bool + IntField int + StringField string + FloatField float64 +} + +type TypeMeta struct { + Fortest bool +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty/zz_generated.defaults.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty/zz_generated.defaults.go new file mode 100644 index 00000000000..4c6ab363bc4 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty/zz_generated.defaults.go @@ -0,0 +1,33 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package empty + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + return nil +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/generate.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/generate.go new file mode 100644 index 00000000000..486ddacf80b --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/generate.go @@ -0,0 +1,27 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2024 The Kubernetes Authors. + +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. +*/ + +// Ignore this file to prevent zz_generated for this package + +//go:generate go run k8s.io/code-generator/cmd/defaulter-gen --output-file zz_generated.defaults.go --go-header-file=../../../examples/hack/boilerplate.go.txt k8s.io/code-generator/cmd/defaulter-gen/output_tests/... +package outputtests + +import ( + // For go-generate + _ "k8s.io/code-generator/cmd/defaulter-gen/generators" +) diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/defaults.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/defaults.go new file mode 100644 index 00000000000..7e27d5412b8 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/defaults.go @@ -0,0 +1,32 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 marker + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +//nolint:unused +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +func SetDefaults_DefaultedWithFunction(obj *DefaultedWithFunction) { + if obj.S1 == "" { + obj.S1 = "default_function" + } +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/doc.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/doc.go new file mode 100644 index 00000000000..b99ba6a6df8 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +// This is a test package. +package marker // import "k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker" diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external/constant.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external/constant.go new file mode 100644 index 00000000000..d04b8e02bff --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external/constant.go @@ -0,0 +1,20 @@ +/* +Copyright 2024 The Kubernetes Authors. + +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 external + +// Used for test with multiple packages of the same name +const AConstant string = "AConstantString" diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external/external/constant.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external/external/constant.go new file mode 100644 index 00000000000..494da105410 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external/external/constant.go @@ -0,0 +1,20 @@ +/* +Copyright 2024 The Kubernetes Authors. + +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 external + +// Used for test with multiple packages of the same name +const AnotherConstant string = "AnotherConstantString" diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external2/type.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external2/type.go new file mode 100644 index 00000000000..df0b94bcbd5 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external2/type.go @@ -0,0 +1,19 @@ +/* +Copyright 2024 The Kubernetes Authors. + +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 external2 + +type String string diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external3/constant.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external3/constant.go new file mode 100644 index 00000000000..a97f4698418 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external3/constant.go @@ -0,0 +1,21 @@ +/* +Copyright 2024 The Kubernetes Authors. + +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 external3 + +import "k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external2" + +type StringPointer *external2.String diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/fake_deepcopy_conversion.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/fake_deepcopy_conversion.go new file mode 100644 index 00000000000..5ccc12b63e0 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/fake_deepcopy_conversion.go @@ -0,0 +1,107 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 marker + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func (in *Defaulted) DeepCopy() *Defaulted { + if in == nil { + return nil + } + out := new(Defaulted) + in.DeepCopyInto(out) + return out +} + +func (in *Defaulted) DeepCopyInto(out *Defaulted) { + *out = *in +} + +func (in *Defaulted) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +func (in *DefaultedOmitempty) DeepCopy() *DefaultedOmitempty { + if in == nil { + return nil + } + out := new(DefaultedOmitempty) + in.DeepCopyInto(out) + return out +} + +func (in *DefaultedOmitempty) DeepCopyInto(out *DefaultedOmitempty) { + *out = *in +} + +func (in *DefaultedOmitempty) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +func (in *DefaultedWithFunction) DeepCopy() *DefaultedWithFunction { + if in == nil { + return nil + } + out := new(DefaultedWithFunction) + in.DeepCopyInto(out) + return out +} + +func (in *DefaultedWithFunction) DeepCopyInto(out *DefaultedWithFunction) { + *out = *in +} + +func (in *DefaultedWithFunction) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +func (in *Defaulted) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } +func (in *DefaultedOmitempty) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } +func (in *DefaultedWithFunction) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } +func (in *DefaultedWithReference) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } + +func (in *DefaultedWithReference) DeepCopy() *DefaultedWithReference { + if in == nil { + return nil + } + out := new(DefaultedWithReference) + in.DeepCopyInto(out) + return out +} + +func (in *DefaultedWithReference) DeepCopyInto(out *DefaultedWithReference) { + *out = *in +} + +func (in *DefaultedWithReference) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/marker_test.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/marker_test.go new file mode 100644 index 00000000000..003ac06cb79 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/marker_test.go @@ -0,0 +1,412 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 marker + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + + "k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external" + externalexternal "k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external/external" + "k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external2" + "k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external3" +) + +func getPointerFromString(s string) *string { + return &s +} + +var ( + defaultInt32 int32 = 32 + defaultInt64 int64 = 64 +) + +func Test_Marker(t *testing.T) { + testcases := []struct { + name string + in Defaulted + out Defaulted + }{ + { + name: "default", + in: Defaulted{}, + out: Defaulted{ + StringDefault: "bar", + StringEmptyDefault: "", + StringEmpty: "", + StringPointer: getPointerFromString("default"), + Int64: &defaultInt64, + Int32: &defaultInt32, + IntDefault: 1, + IntEmptyDefault: 0, + IntEmpty: 0, + FloatDefault: 0.5, + FloatEmptyDefault: 0.0, + FloatEmpty: 0.0, + List: []Item{ + getPointerFromString("foo"), + getPointerFromString("bar"), + }, + Sub: &SubStruct{ + S: "foo", + I: 5, + }, + OtherSub: SubStruct{ + S: "", + I: 1, + }, + StructList: []SubStruct{ + { + S: "foo1", + I: 1, + }, + { + S: "foo2", + I: 1, + }, + }, + PtrStructList: []*SubStruct{ + { + S: "foo1", + I: 1, + }, + { + S: "foo2", + I: 1, + }, + }, + StringList: []string{ + "foo", + }, + Map: map[string]Item{ + "foo": getPointerFromString("bar"), + }, + StructMap: map[string]SubStruct{ + "foo": { + S: "string", + I: 1, + }, + }, + PtrStructMap: map[string]*SubStruct{ + "foo": { + S: "string", + I: 1, + }, + }, + AliasPtr: getPointerFromString("banana"), + }, + }, + { + name: "values-omitempty", + in: Defaulted{ + StringDefault: "changed", + IntDefault: 5, + }, + out: Defaulted{ + StringDefault: "changed", + StringEmptyDefault: "", + StringEmpty: "", + StringPointer: getPointerFromString("default"), + Int64: &defaultInt64, + Int32: &defaultInt32, + IntDefault: 5, + IntEmptyDefault: 0, + IntEmpty: 0, + FloatDefault: 0.5, + FloatEmptyDefault: 0.0, + FloatEmpty: 0.0, + List: []Item{ + getPointerFromString("foo"), + getPointerFromString("bar"), + }, + Sub: &SubStruct{ + S: "foo", + I: 5, + }, + StructList: []SubStruct{ + { + S: "foo1", + I: 1, + }, + { + S: "foo2", + I: 1, + }, + }, + PtrStructList: []*SubStruct{ + { + S: "foo1", + I: 1, + }, + { + S: "foo2", + I: 1, + }, + }, + StringList: []string{ + "foo", + }, + OtherSub: SubStruct{ + S: "", + I: 1, + }, + Map: map[string]Item{ + "foo": getPointerFromString("bar"), + }, + StructMap: map[string]SubStruct{ + "foo": { + S: "string", + I: 1, + }, + }, + PtrStructMap: map[string]*SubStruct{ + "foo": { + S: "string", + I: 1, + }, + }, + AliasPtr: getPointerFromString("banana"), + }, + }, + { + name: "lists", + in: Defaulted{ + List: []Item{ + nil, + getPointerFromString("bar"), + }, + }, + out: Defaulted{ + StringDefault: "bar", + StringEmptyDefault: "", + StringEmpty: "", + StringPointer: getPointerFromString("default"), + Int64: &defaultInt64, + Int32: &defaultInt32, + IntDefault: 1, + IntEmptyDefault: 0, + IntEmpty: 0, + FloatDefault: 0.5, + FloatEmptyDefault: 0.0, + FloatEmpty: 0.0, + List: []Item{ + getPointerFromString("apple"), + getPointerFromString("bar"), + }, + Sub: &SubStruct{ + S: "foo", + I: 5, + }, + StructList: []SubStruct{ + { + S: "foo1", + I: 1, + }, + { + S: "foo2", + I: 1, + }, + }, + PtrStructList: []*SubStruct{ + { + S: "foo1", + I: 1, + }, + { + S: "foo2", + I: 1, + }, + }, + StringList: []string{ + "foo", + }, + OtherSub: SubStruct{ + S: "", + I: 1, + }, + Map: map[string]Item{ + "foo": getPointerFromString("bar"), + }, + StructMap: map[string]SubStruct{ + "foo": { + S: "string", + I: 1, + }, + }, + PtrStructMap: map[string]*SubStruct{ + "foo": { + S: "string", + I: 1, + }, + }, + AliasPtr: getPointerFromString("banana"), + }, + }, + { + name: "stringmap", + in: Defaulted{ + Map: map[string]Item{ + "foo": nil, + "bar": getPointerFromString("banana"), + }, + }, + out: Defaulted{ + StringDefault: "bar", + StringEmptyDefault: "", + StringEmpty: "", + StringPointer: getPointerFromString("default"), + Int64: &defaultInt64, + Int32: &defaultInt32, + IntDefault: 1, + IntEmptyDefault: 0, + IntEmpty: 0, + FloatDefault: 0.5, + FloatEmptyDefault: 0.0, + FloatEmpty: 0.0, + List: []Item{ + getPointerFromString("foo"), + getPointerFromString("bar"), + }, + Sub: &SubStruct{ + S: "foo", + I: 5, + }, + StructList: []SubStruct{ + { + S: "foo1", + I: 1, + }, + { + S: "foo2", + I: 1, + }, + }, + PtrStructList: []*SubStruct{ + { + S: "foo1", + I: 1, + }, + { + S: "foo2", + I: 1, + }, + }, + StringList: []string{ + "foo", + }, + OtherSub: SubStruct{ + S: "", + I: 1, + }, + Map: map[string]Item{ + "foo": getPointerFromString("apple"), + "bar": getPointerFromString("banana"), + }, + StructMap: map[string]SubStruct{ + "foo": { + S: "string", + I: 1, + }, + }, + PtrStructMap: map[string]*SubStruct{ + "foo": { + S: "string", + I: 1, + }, + }, + AliasPtr: getPointerFromString("banana"), + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + SetObjectDefaults_Defaulted(&tc.in) + if diff := cmp.Diff(tc.out, tc.in); len(diff) > 0 { + t.Errorf("Error: Expected and actual output are different \n %s\n", diff) + } + }) + } +} + +func Test_DefaultingFunction(t *testing.T) { + in := DefaultedWithFunction{} + SetObjectDefaults_DefaultedWithFunction(&in) + out := DefaultedWithFunction{ + S1: "default_function", + S2: "default_marker", + } + if diff := cmp.Diff(out, in); len(diff) > 0 { + t.Errorf("Error: Expected and actual output are different \n %s\n", diff) + } + +} + +func Test_DefaultingReference(t *testing.T) { + dv := DefaultedValueItem(SomeValue) + SomeDefault := SomeDefault + SomeValue := SomeValue + + ptrVar9 := string(SomeValue) + ptrVar8 := &ptrVar9 + ptrVar7 := (*B1)(&ptrVar8) + ptrVar6 := (*B2)(&ptrVar7) + ptrVar5 := &ptrVar6 + ptrVar4 := &ptrVar5 + ptrVar3 := &ptrVar4 + ptrVar2 := (*B3)(&ptrVar3) + ptrVar1 := &ptrVar2 + + var external2Str = external2.String(SomeValue) + + testcases := []struct { + name string + in DefaultedWithReference + out DefaultedWithReference + }{ + { + name: "default", + in: DefaultedWithReference{}, + out: DefaultedWithReference{ + AliasPointerInside: Item(&SomeDefault), + AliasOverride: Item(&SomeDefault), + AliasConvertDefaultPointer: &dv, + AliasPointerDefault: &dv, + PointerAliasDefault: Item(getPointerFromString("apple")), + AliasNonPointer: SomeValue, + AliasPointer: &SomeValue, + SymbolReference: SomeDefault, + SameNamePackageSymbolReference1: external.AConstant, + SameNamePackageSymbolReference2: externalexternal.AnotherConstant, + PointerConversion: (*B4)(&ptrVar1), + PointerConversionValue: (B4)(ptrVar1), + FullyQualifiedLocalSymbol: string(SomeValue), + ImportFromAliasCast: external3.StringPointer(&external2Str), + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + SetObjectDefaults_DefaultedWithReference(&tc.in) + if diff := cmp.Diff(tc.out, tc.in); len(diff) > 0 { + t.Errorf("Error: Expected and actual output are different \n %s\n", diff) + } + }) + } +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/type.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/type.go new file mode 100644 index 00000000000..0658ba217c0 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/type.go @@ -0,0 +1,265 @@ +/* +Copyright 2020 The Kubernetes Authors. + +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 marker + +import ( + "k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty" + "k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external3" +) + +type Defaulted struct { + empty.TypeMeta + + // +default="bar" + StringDefault string + + // Default is forced to empty string + // Specifying the default is a no-op + // +default="" + StringEmptyDefault string + + // Not specifying a default still defaults for non-omitempty + StringEmpty string + + // +default="default" + StringPointer *string + + // +default=64 + Int64 *int64 + + // +default=32 + Int32 *int32 + + // +default=1 + IntDefault int + + // +default=0 + IntEmptyDefault int + + // Default is forced to 0 + IntEmpty int + + // +default=0.5 + FloatDefault float64 + + // +default=0.0 + FloatEmptyDefault float64 + + FloatEmpty float64 + + // +default=["foo", "bar"] + List []Item + // +default={"s": "foo", "i": 5} + Sub *SubStruct + + //+default=[{"s": "foo1", "i": 1}, {"s": "foo2"}] + StructList []SubStruct + + //+default=[{"s": "foo1", "i": 1}, {"s": "foo2"}] + PtrStructList []*SubStruct + + //+default=["foo"] + StringList []string + + // Default is forced to empty struct + OtherSub SubStruct + + // +default={"foo": "bar"} + Map map[string]Item + + // +default={"foo": {"S": "string", "I": 1}} + StructMap map[string]SubStruct + + // +default={"foo": {"S": "string", "I": 1}} + PtrStructMap map[string]*SubStruct + + // A default specified here overrides the default for the Item type + // +default="banana" + AliasPtr Item +} + +type DefaultedOmitempty struct { + empty.TypeMeta `json:",omitempty"` + + // +default="bar" + StringDefault string `json:",omitempty"` + + // Default is forced to empty string + // Specifying the default is a no-op + // +default="" + StringEmptyDefault string `json:",omitempty"` + + // Not specifying a default still defaults for non-omitempty + StringEmpty string `json:",omitempty"` + + // +default="default" + StringPointer *string `json:",omitempty"` + + // +default=64 + Int64 *int64 `json:",omitempty"` + + // +default=32 + Int32 *int32 `json:",omitempty"` + + // +default=1 + IntDefault int `json:",omitempty"` + + // +default=0 + IntEmptyDefault int `json:",omitempty"` + + // Default is forced to 0 + IntEmpty int `json:",omitempty"` + + // +default=0.5 + FloatDefault float64 `json:",omitempty"` + + // +default=0.0 + FloatEmptyDefault float64 `json:",omitempty"` + + FloatEmpty float64 `json:",omitempty"` + + // +default=["foo", "bar"] + List []Item `json:",omitempty"` + // +default={"s": "foo", "i": 5} + Sub *SubStruct `json:",omitempty"` + + //+default=[{"s": "foo1", "i": 1}, {"s": "foo2"}] + StructList []SubStruct `json:",omitempty"` + + //+default=[{"s": "foo1", "i": 1}, {"s": "foo2"}] + PtrStructList []*SubStruct `json:",omitempty"` + + //+default=["foo"] + StringList []string `json:",omitempty"` + + // Default is forced to empty struct + OtherSub SubStruct `json:",omitempty"` + + // +default={"foo": "bar"} + Map map[string]Item `json:",omitempty"` + + // +default={"foo": {"S": "string", "I": 1}} + StructMap map[string]SubStruct `json:",omitempty"` + + // +default={"foo": {"S": "string", "I": 1}} + PtrStructMap map[string]*SubStruct `json:",omitempty"` + + // A default specified here overrides the default for the Item type + // +default="banana" + AliasPtr Item `json:",omitempty"` +} + +const SomeDefault = "ACoolConstant" + +// +default="apple" +type Item *string + +type ValueItem string + +// +default=ref(SomeValue) +type DefaultedValueItem ValueItem +type PointerValueItem *DefaultedValueItem + +type ItemDefaultWiped Item + +const SomeValue ValueItem = "Value" + +type SubStruct struct { + S string + // +default=1 + I int `json:"I,omitempty"` +} + +type DefaultedWithFunction struct { + empty.TypeMeta + // +default="default_marker" + S1 string `json:"S1,omitempty"` + // +default="default_marker" + S2 string `json:"S2,omitempty"` +} + +type DefaultedWithReference struct { + empty.TypeMeta + + // Shows that if we have an alias that is a pointer and have a default + // that is a value convertible to that pointer we can still use it + // +default=ref(SomeValue) + AliasConvertDefaultPointer PointerValueItem + + // Shows that default defined on a nested type is not respected through + // an alias + AliasWipedDefault ItemDefaultWiped + + // A default defined on a pointer-valued alias is respected + PointerAliasDefault Item + + // Can have alias that is a pointer to type of constant + // +default=ref(SomeDefault) + AliasPointerInside Item + + // Can override default specified on an alias + // +default=ref(SomeDefault) + AliasOverride Item + + // Type-level default is not respected unless a pointer + AliasNonPointerDefault DefaultedValueItem `json:",omitempty"` + + // Type-level default is not respected unless a pointer + AliasPointerDefault *DefaultedValueItem + + // Can have value typed alias + // +default=ref(SomeValue) + AliasNonPointer ValueItem `json:",omitempty"` + + // Can have a pointer to an alias whose default is a non-pointer value + // +default=ref(SomeValue) + AliasPointer *ValueItem `json:",omitempty"` + + // Basic ref usage example + // +default=ref(SomeDefault) + SymbolReference string `json:",omitempty"` + + // +default=ref(k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external.AConstant) + SameNamePackageSymbolReference1 string `json:",omitempty"` + + // +default=ref(k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external/external.AnotherConstant) + SameNamePackageSymbolReference2 string `json:",omitempty"` + + // Should convert ValueItem -> string then up to B4 through addressOf and + // casting + // +default=ref(SomeValue) + PointerConversion *B4 + + // +default=ref(SomeValue) + PointerConversionValue B4 + + // +default=ref(k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker.SomeValue) + FullyQualifiedLocalSymbol string + + // Construction of external3.StringPointer requires importing external2 + // Test that generator can handle it + // +default=ref(SomeValue) + ImportFromAliasCast external3.StringPointer +} + +// Super complicated hierarchy of aliases which includes multiple pointers, +// and sibling types. +type B0 *string +type B1 B0 +type B2 *B1 +type B3 ****B2 +type B4 **B3 diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/zz_generated.defaults.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/zz_generated.defaults.go new file mode 100644 index 00000000000..973e71fced8 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/zz_generated.defaults.go @@ -0,0 +1,325 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package marker + +import ( + "encoding/json" + + runtime "k8s.io/apimachinery/pkg/runtime" + external "k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external" + externalexternal "k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external/external" + external2 "k8s.io/code-generator/cmd/defaulter-gen/output_tests/marker/external2" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&Defaulted{}, func(obj interface{}) { SetObjectDefaults_Defaulted(obj.(*Defaulted)) }) + scheme.AddTypeDefaultingFunc(&DefaultedOmitempty{}, func(obj interface{}) { SetObjectDefaults_DefaultedOmitempty(obj.(*DefaultedOmitempty)) }) + scheme.AddTypeDefaultingFunc(&DefaultedWithFunction{}, func(obj interface{}) { SetObjectDefaults_DefaultedWithFunction(obj.(*DefaultedWithFunction)) }) + scheme.AddTypeDefaultingFunc(&DefaultedWithReference{}, func(obj interface{}) { SetObjectDefaults_DefaultedWithReference(obj.(*DefaultedWithReference)) }) + return nil +} + +func SetObjectDefaults_Defaulted(in *Defaulted) { + if in.StringDefault == "" { + in.StringDefault = "bar" + } + if in.StringPointer == nil { + var ptrVar1 string = "default" + in.StringPointer = &ptrVar1 + } + if in.Int64 == nil { + var ptrVar1 int64 = 64 + in.Int64 = &ptrVar1 + } + if in.Int32 == nil { + var ptrVar1 int32 = 32 + in.Int32 = &ptrVar1 + } + if in.IntDefault == 0 { + in.IntDefault = 1 + } + if in.FloatDefault == 0 { + in.FloatDefault = 0.5 + } + if in.List == nil { + if err := json.Unmarshal([]byte(`["foo", "bar"]`), &in.List); err != nil { + panic(err) + } + } + for i := range in.List { + if in.List[i] == nil { + var ptrVar1 string = "apple" + in.List[i] = &ptrVar1 + } + } + if in.Sub == nil { + if err := json.Unmarshal([]byte(`{"s": "foo", "i": 5}`), &in.Sub); err != nil { + panic(err) + } + } + if in.Sub != nil { + if in.Sub.I == 0 { + in.Sub.I = 1 + } + } + if in.StructList == nil { + if err := json.Unmarshal([]byte(`[{"s": "foo1", "i": 1}, {"s": "foo2"}]`), &in.StructList); err != nil { + panic(err) + } + } + for i := range in.StructList { + a := &in.StructList[i] + if a.I == 0 { + a.I = 1 + } + } + if in.PtrStructList == nil { + if err := json.Unmarshal([]byte(`[{"s": "foo1", "i": 1}, {"s": "foo2"}]`), &in.PtrStructList); err != nil { + panic(err) + } + } + for i := range in.PtrStructList { + a := in.PtrStructList[i] + if a != nil { + if a.I == 0 { + a.I = 1 + } + } + } + if in.StringList == nil { + if err := json.Unmarshal([]byte(`["foo"]`), &in.StringList); err != nil { + panic(err) + } + } + if in.OtherSub.I == 0 { + in.OtherSub.I = 1 + } + if in.Map == nil { + if err := json.Unmarshal([]byte(`{"foo": "bar"}`), &in.Map); err != nil { + panic(err) + } + } + for i_Map := range in.Map { + if in.Map[i_Map] == nil { + var ptrVar1 string = "apple" + in.Map[i_Map] = &ptrVar1 + } + } + if in.StructMap == nil { + if err := json.Unmarshal([]byte(`{"foo": {"S": "string", "I": 1}}`), &in.StructMap); err != nil { + panic(err) + } + } + if in.PtrStructMap == nil { + if err := json.Unmarshal([]byte(`{"foo": {"S": "string", "I": 1}}`), &in.PtrStructMap); err != nil { + panic(err) + } + } + if in.AliasPtr == nil { + var ptrVar1 string = "banana" + in.AliasPtr = &ptrVar1 + } +} + +func SetObjectDefaults_DefaultedOmitempty(in *DefaultedOmitempty) { + if in.StringDefault == "" { + in.StringDefault = "bar" + } + if in.StringPointer == nil { + var ptrVar1 string = "default" + in.StringPointer = &ptrVar1 + } + if in.Int64 == nil { + var ptrVar1 int64 = 64 + in.Int64 = &ptrVar1 + } + if in.Int32 == nil { + var ptrVar1 int32 = 32 + in.Int32 = &ptrVar1 + } + if in.IntDefault == 0 { + in.IntDefault = 1 + } + if in.FloatDefault == 0 { + in.FloatDefault = 0.5 + } + if in.List == nil { + if err := json.Unmarshal([]byte(`["foo", "bar"]`), &in.List); err != nil { + panic(err) + } + } + for i := range in.List { + if in.List[i] == nil { + var ptrVar1 string = "apple" + in.List[i] = &ptrVar1 + } + } + if in.Sub == nil { + if err := json.Unmarshal([]byte(`{"s": "foo", "i": 5}`), &in.Sub); err != nil { + panic(err) + } + } + if in.Sub != nil { + if in.Sub.I == 0 { + in.Sub.I = 1 + } + } + if in.StructList == nil { + if err := json.Unmarshal([]byte(`[{"s": "foo1", "i": 1}, {"s": "foo2"}]`), &in.StructList); err != nil { + panic(err) + } + } + for i := range in.StructList { + a := &in.StructList[i] + if a.I == 0 { + a.I = 1 + } + } + if in.PtrStructList == nil { + if err := json.Unmarshal([]byte(`[{"s": "foo1", "i": 1}, {"s": "foo2"}]`), &in.PtrStructList); err != nil { + panic(err) + } + } + for i := range in.PtrStructList { + a := in.PtrStructList[i] + if a != nil { + if a.I == 0 { + a.I = 1 + } + } + } + if in.StringList == nil { + if err := json.Unmarshal([]byte(`["foo"]`), &in.StringList); err != nil { + panic(err) + } + } + if in.OtherSub.I == 0 { + in.OtherSub.I = 1 + } + if in.Map == nil { + if err := json.Unmarshal([]byte(`{"foo": "bar"}`), &in.Map); err != nil { + panic(err) + } + } + for i_Map := range in.Map { + if in.Map[i_Map] == nil { + var ptrVar1 string = "apple" + in.Map[i_Map] = &ptrVar1 + } + } + if in.StructMap == nil { + if err := json.Unmarshal([]byte(`{"foo": {"S": "string", "I": 1}}`), &in.StructMap); err != nil { + panic(err) + } + } + if in.PtrStructMap == nil { + if err := json.Unmarshal([]byte(`{"foo": {"S": "string", "I": 1}}`), &in.PtrStructMap); err != nil { + panic(err) + } + } + if in.AliasPtr == nil { + var ptrVar1 string = "banana" + in.AliasPtr = &ptrVar1 + } +} + +func SetObjectDefaults_DefaultedWithFunction(in *DefaultedWithFunction) { + SetDefaults_DefaultedWithFunction(in) + if in.S1 == "" { + in.S1 = "default_marker" + } + if in.S2 == "" { + in.S2 = "default_marker" + } +} + +func SetObjectDefaults_DefaultedWithReference(in *DefaultedWithReference) { + if in.AliasConvertDefaultPointer == nil { + ptrVar1 := DefaultedValueItem(SomeValue) + in.AliasConvertDefaultPointer = &ptrVar1 + } + if in.PointerAliasDefault == nil { + var ptrVar1 string = "apple" + in.PointerAliasDefault = &ptrVar1 + } + if in.AliasPointerInside == nil { + ptrVar1 := string(SomeDefault) + in.AliasPointerInside = &ptrVar1 + } + if in.AliasOverride == nil { + ptrVar1 := string(SomeDefault) + in.AliasOverride = &ptrVar1 + } + if in.AliasPointerDefault == nil { + ptrVar1 := DefaultedValueItem(SomeValue) + in.AliasPointerDefault = &ptrVar1 + } + if in.AliasNonPointer == "" { + in.AliasNonPointer = ValueItem(SomeValue) + } + if in.AliasPointer == nil { + ptrVar1 := ValueItem(SomeValue) + in.AliasPointer = &ptrVar1 + } + if in.SymbolReference == "" { + in.SymbolReference = string(SomeDefault) + } + if in.SameNamePackageSymbolReference1 == "" { + in.SameNamePackageSymbolReference1 = string(external.AConstant) + } + if in.SameNamePackageSymbolReference2 == "" { + in.SameNamePackageSymbolReference2 = string(externalexternal.AnotherConstant) + } + if in.PointerConversion == nil { + ptrVar9 := string(SomeValue) + ptrVar8 := &ptrVar9 + ptrVar7 := (*B1)(&ptrVar8) + ptrVar6 := (*B2)(&ptrVar7) + ptrVar5 := &ptrVar6 + ptrVar4 := &ptrVar5 + ptrVar3 := &ptrVar4 + ptrVar2 := (*B3)(&ptrVar3) + ptrVar1 := &ptrVar2 + in.PointerConversion = (*B4)(&ptrVar1) + } + if in.PointerConversionValue == nil { + ptrVar8 := string(SomeValue) + ptrVar7 := &ptrVar8 + ptrVar6 := (*B1)(&ptrVar7) + ptrVar5 := (*B2)(&ptrVar6) + ptrVar4 := &ptrVar5 + ptrVar3 := &ptrVar4 + ptrVar2 := &ptrVar3 + ptrVar1 := (*B3)(&ptrVar2) + in.PointerConversionValue = &ptrVar1 + } + if in.FullyQualifiedLocalSymbol == "" { + in.FullyQualifiedLocalSymbol = string(SomeValue) + } + if in.ImportFromAliasCast == nil { + ptrVar1 := external2.String(SomeValue) + in.ImportFromAliasCast = &ptrVar1 + } +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/defaults.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/defaults.go new file mode 100644 index 00000000000..e656f5037c5 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/defaults.go @@ -0,0 +1,33 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 pointer + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +//nolint:unused +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +func SetDefaults_Tpointer(obj *Tpointer) { + if obj.BoolField == nil { + obj.BoolField = new(bool) + *obj.BoolField = true + } +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/doc.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/doc.go new file mode 100644 index 00000000000..0585a1b707b --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +// This is a test package. +package pointer // import "k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer" diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/fake_deepcopy_conversion.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/fake_deepcopy_conversion.go new file mode 100644 index 00000000000..c74e764bb84 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/fake_deepcopy_conversion.go @@ -0,0 +1,82 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 pointer + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Tpointer) DeepCopyInto(out *Tpointer) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.BoolField != nil { + in, out := &in.BoolField, &out.BoolField + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Tpointer. +func (in *Tpointer) DeepCopy() *Tpointer { + if in == nil { + return nil + } + out := new(Tpointer) + in.DeepCopyInto(out) + return out +} + +func (in *Tpointer) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Ttest) DeepCopyInto(out *Ttest) { + *out = *in + out.TypeMeta = in.TypeMeta + in.NTP.DeepCopyInto(&out.NTP) + if in.Tp != nil { + in, out := &in.Tp, &out.Tp + *out = new(Tpointer) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Ttest. +func (in *Ttest) DeepCopy() *Ttest { + if in == nil { + return nil + } + out := new(Ttest) + in.DeepCopyInto(out) + return out +} + +func (in *Ttest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +func (in *Tpointer) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } +func (in *Ttest) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/type.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/type.go new file mode 100644 index 00000000000..5398a42aaec --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/type.go @@ -0,0 +1,33 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 pointer + +import ( + "k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty" +) + +type Tpointer struct { + empty.TypeMeta + BoolField *bool +} + +// Only test +type Ttest struct { + empty.TypeMeta + NTP Tpointer + Tp *Tpointer +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/zz_generated.defaults.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/zz_generated.defaults.go new file mode 100644 index 00000000000..8bee913dbf6 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/pointer/zz_generated.defaults.go @@ -0,0 +1,46 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package pointer + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&Tpointer{}, func(obj interface{}) { SetObjectDefaults_Tpointer(obj.(*Tpointer)) }) + scheme.AddTypeDefaultingFunc(&Ttest{}, func(obj interface{}) { SetObjectDefaults_Ttest(obj.(*Ttest)) }) + return nil +} + +func SetObjectDefaults_Tpointer(in *Tpointer) { + SetDefaults_Tpointer(in) +} + +func SetObjectDefaults_Ttest(in *Ttest) { + SetObjectDefaults_Tpointer(&in.NTP) + if in.Tp != nil { + SetObjectDefaults_Tpointer(in.Tp) + } +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/defaults.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/defaults.go new file mode 100644 index 00000000000..21a2ee25c38 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/defaults.go @@ -0,0 +1,33 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 slices + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +//nolint:unused +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +func SetDefaults_Ttest(obj *Ttest) { + if obj.BoolField == nil { + obj.BoolField = new(bool) + *obj.BoolField = true + } +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/doc.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/doc.go new file mode 100644 index 00000000000..c341905023a --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +// This is a test package. +package slices // import "k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices" diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/fake_deepcopy_conversion.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/fake_deepcopy_conversion.go new file mode 100644 index 00000000000..ab07e3da7cc --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/fake_deepcopy_conversion.go @@ -0,0 +1,118 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 slices + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Ttest) DeepCopyInto(out *Ttest) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.BoolField != nil { + in, out := &in.BoolField, &out.BoolField + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Ttest. +func (in *Ttest) DeepCopy() *Ttest { + if in == nil { + return nil + } + out := new(Ttest) + in.DeepCopyInto(out) + return out +} + +func (in *Ttest) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TtestList) DeepCopyInto(out *TtestList) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Ttest, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TtestList. +func (in *TtestList) DeepCopy() *TtestList { + if in == nil { + return nil + } + out := new(TtestList) + in.DeepCopyInto(out) + return out +} + +func (in *TtestList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TtestPointerList) DeepCopyInto(out *TtestPointerList) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]*Ttest, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Ttest) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TtestPointerList. +func (in *TtestPointerList) DeepCopy() *TtestPointerList { + if in == nil { + return nil + } + out := new(TtestPointerList) + in.DeepCopyInto(out) + return out +} + +func (in *TtestPointerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +func (in *Ttest) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } +func (in *TtestList) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } +func (in *TtestPointerList) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/type.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/type.go new file mode 100644 index 00000000000..0b0a79f4f69 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/type.go @@ -0,0 +1,37 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 slices + +import ( + "k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty" +) + +// Only test +type Ttest struct { + empty.TypeMeta + BoolField *bool +} + +type TtestList struct { + empty.TypeMeta + Items []Ttest +} + +type TtestPointerList struct { + empty.TypeMeta + Items []*Ttest +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/zz_generated.defaults.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/zz_generated.defaults.go new file mode 100644 index 00000000000..a7d6e2678f9 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/slices/zz_generated.defaults.go @@ -0,0 +1,56 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package slices + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&Ttest{}, func(obj interface{}) { SetObjectDefaults_Ttest(obj.(*Ttest)) }) + scheme.AddTypeDefaultingFunc(&TtestList{}, func(obj interface{}) { SetObjectDefaults_TtestList(obj.(*TtestList)) }) + scheme.AddTypeDefaultingFunc(&TtestPointerList{}, func(obj interface{}) { SetObjectDefaults_TtestPointerList(obj.(*TtestPointerList)) }) + return nil +} + +func SetObjectDefaults_Ttest(in *Ttest) { + SetDefaults_Ttest(in) +} + +func SetObjectDefaults_TtestList(in *TtestList) { + for i := range in.Items { + a := &in.Items[i] + SetObjectDefaults_Ttest(a) + } +} + +func SetObjectDefaults_TtestPointerList(in *TtestPointerList) { + for i := range in.Items { + a := in.Items[i] + if a != nil { + SetObjectDefaults_Ttest(a) + } + } +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/defaults.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/defaults.go new file mode 100644 index 00000000000..bcfdde1a32e --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/defaults.go @@ -0,0 +1,33 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 wholepkg + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +//nolint:unused +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +func SetDefaults_StructPrimitives(obj *StructPrimitives) { + if obj.BoolField == nil { + obj.BoolField = new(bool) + *obj.BoolField = true + } +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/doc.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/doc.go new file mode 100644 index 00000000000..d9a62999717 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +// This is a test package. +package wholepkg // import "k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg" diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/fake_deepcopy_conversion.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/fake_deepcopy_conversion.go new file mode 100644 index 00000000000..e896bf72684 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/fake_deepcopy_conversion.go @@ -0,0 +1,315 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 wholepkg + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StructEverything) DeepCopyInto(out *StructEverything) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.BoolPtrField != nil { + in, out := &in.BoolPtrField, &out.BoolPtrField + *out = new(bool) + **out = **in + } + if in.IntPtrField != nil { + in, out := &in.IntPtrField, &out.IntPtrField + *out = new(int) + **out = **in + } + if in.StringPtrField != nil { + in, out := &in.StringPtrField, &out.StringPtrField + *out = new(string) + **out = **in + } + if in.FloatPtrField != nil { + in, out := &in.FloatPtrField, &out.FloatPtrField + *out = new(float64) + **out = **in + } + in.PointerStructField.DeepCopyInto(&out.PointerStructField) + if in.SliceBoolField != nil { + in, out := &in.SliceBoolField, &out.SliceBoolField + *out = make([]bool, len(*in)) + copy(*out, *in) + } + if in.SliceByteField != nil { + in, out := &in.SliceByteField, &out.SliceByteField + *out = make([]byte, len(*in)) + copy(*out, *in) + } + if in.SliceIntField != nil { + in, out := &in.SliceIntField, &out.SliceIntField + *out = make([]int, len(*in)) + copy(*out, *in) + } + if in.SliceStringField != nil { + in, out := &in.SliceStringField, &out.SliceStringField + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.SliceFloatField != nil { + in, out := &in.SliceFloatField, &out.SliceFloatField + *out = make([]float64, len(*in)) + copy(*out, *in) + } + in.SlicesStructField.DeepCopyInto(&out.SlicesStructField) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StructEverything. +func (in *StructEverything) DeepCopy() *StructEverything { + if in == nil { + return nil + } + out := new(StructEverything) + in.DeepCopyInto(out) + return out +} + +func (in *StructEverything) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StructPointer) DeepCopyInto(out *StructPointer) { + *out = *in + out.TypeMeta = in.TypeMeta + in.PointerStructPrimitivesField.DeepCopyInto(&out.PointerStructPrimitivesField) + if in.PointerPointerStructPrimitivesField != nil { + in, out := &in.PointerPointerStructPrimitivesField, &out.PointerPointerStructPrimitivesField + *out = new(StructPrimitives) + (*in).DeepCopyInto(*out) + } + in.PointerStructPrimitivesAliasField.DeepCopyInto(&out.PointerStructPrimitivesAliasField) + in.PointerPointerStructPrimitivesAliasField.DeepCopyInto(&out.PointerPointerStructPrimitivesAliasField) + in.PointerStructStructPrimitives.DeepCopyInto(&out.PointerStructStructPrimitives) + if in.PointerPointerStructStructPrimitives != nil { + in, out := &in.PointerPointerStructStructPrimitives, &out.PointerPointerStructStructPrimitives + *out = new(StructStructPrimitives) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StructPointer. +func (in *StructPointer) DeepCopy() *StructPointer { + if in == nil { + return nil + } + out := new(StructPointer) + in.DeepCopyInto(out) + return out +} + +func (in *StructPointer) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StructPrimitives) DeepCopyInto(out *StructPrimitives) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.BoolField != nil { + in, out := &in.BoolField, &out.BoolField + *out = new(bool) + **out = **in + } + if in.IntField != nil { + in, out := &in.IntField, &out.IntField + *out = new(int) + **out = **in + } + if in.StringField != nil { + in, out := &in.StringField, &out.StringField + *out = new(string) + **out = **in + } + if in.FloatField != nil { + in, out := &in.FloatField, &out.FloatField + *out = new(float64) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StructPrimitives. +func (in *StructPrimitives) DeepCopy() *StructPrimitives { + if in == nil { + return nil + } + out := new(StructPrimitives) + in.DeepCopyInto(out) + return out +} + +func (in *StructPrimitives) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StructPrimitivesAlias) DeepCopyInto(out *StructPrimitivesAlias) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.BoolField != nil { + in, out := &in.BoolField, &out.BoolField + *out = new(bool) + **out = **in + } + if in.IntField != nil { + in, out := &in.IntField, &out.IntField + *out = new(int) + **out = **in + } + if in.StringField != nil { + in, out := &in.StringField, &out.StringField + *out = new(string) + **out = **in + } + if in.FloatField != nil { + in, out := &in.FloatField, &out.FloatField + *out = new(float64) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StructPrimitivesAlias. +func (in *StructPrimitivesAlias) DeepCopy() *StructPrimitivesAlias { + if in == nil { + return nil + } + out := new(StructPrimitivesAlias) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StructSlices) DeepCopyInto(out *StructSlices) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.SliceStructPrimitivesField != nil { + in, out := &in.SliceStructPrimitivesField, &out.SliceStructPrimitivesField + *out = make([]StructPrimitives, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SlicePointerStructPrimitivesField != nil { + in, out := &in.SlicePointerStructPrimitivesField, &out.SlicePointerStructPrimitivesField + *out = make([]*StructPrimitives, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StructPrimitives) + (*in).DeepCopyInto(*out) + } + } + } + if in.SliceStructPrimitivesAliasField != nil { + in, out := &in.SliceStructPrimitivesAliasField, &out.SliceStructPrimitivesAliasField + *out = make([]StructPrimitivesAlias, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SlicePointerStructPrimitivesAliasField != nil { + in, out := &in.SlicePointerStructPrimitivesAliasField, &out.SlicePointerStructPrimitivesAliasField + *out = make([]*StructPrimitivesAlias, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StructPrimitivesAlias) + (*in).DeepCopyInto(*out) + } + } + } + if in.SliceStructStructPrimitives != nil { + in, out := &in.SliceStructStructPrimitives, &out.SliceStructStructPrimitives + *out = make([]StructStructPrimitives, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SlicePointerStructStructPrimitives != nil { + in, out := &in.SlicePointerStructStructPrimitives, &out.SlicePointerStructStructPrimitives + *out = make([]*StructStructPrimitives, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(StructStructPrimitives) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StructSlices. +func (in *StructSlices) DeepCopy() *StructSlices { + if in == nil { + return nil + } + out := new(StructSlices) + in.DeepCopyInto(out) + return out +} + +func (in *StructSlices) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StructStructPrimitives) DeepCopyInto(out *StructStructPrimitives) { + *out = *in + out.TypeMeta = in.TypeMeta + in.StructField.DeepCopyInto(&out.StructField) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StructStructPrimitives. +func (in *StructStructPrimitives) DeepCopy() *StructStructPrimitives { + if in == nil { + return nil + } + out := new(StructStructPrimitives) + in.DeepCopyInto(out) + return out +} + +func (in *StructStructPrimitives) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +func (in *StructEverything) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } +func (in *StructPointer) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } +func (in *StructPrimitives) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } +func (in *StructSlices) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } +func (in *StructStructPrimitives) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/type.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/type.go new file mode 100644 index 00000000000..80a83b74659 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/type.go @@ -0,0 +1,74 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 wholepkg + +import ( + "k8s.io/code-generator/cmd/defaulter-gen/output_tests/empty" +) + +// Only primitives +type StructPrimitives struct { + empty.TypeMeta + BoolField *bool + IntField *int + StringField *string + FloatField *float64 +} +type StructPrimitivesAlias StructPrimitives + +type StructStructPrimitives struct { + empty.TypeMeta + StructField StructPrimitives +} + +// Pointer +type StructPointer struct { + empty.TypeMeta + PointerStructPrimitivesField StructPrimitives + PointerPointerStructPrimitivesField *StructPrimitives + PointerStructPrimitivesAliasField StructPrimitivesAlias + PointerPointerStructPrimitivesAliasField StructPrimitivesAlias + PointerStructStructPrimitives StructStructPrimitives + PointerPointerStructStructPrimitives *StructStructPrimitives +} + +// Slices +type StructSlices struct { + empty.TypeMeta + SliceStructPrimitivesField []StructPrimitives + SlicePointerStructPrimitivesField []*StructPrimitives + SliceStructPrimitivesAliasField []StructPrimitivesAlias + SlicePointerStructPrimitivesAliasField []*StructPrimitivesAlias + SliceStructStructPrimitives []StructStructPrimitives + SlicePointerStructStructPrimitives []*StructStructPrimitives +} + +// Everything +type StructEverything struct { + empty.TypeMeta + BoolPtrField *bool + IntPtrField *int + StringPtrField *string + FloatPtrField *float64 + PointerStructField StructPointer + SliceBoolField []bool + SliceByteField []byte + SliceIntField []int + SliceStringField []string + SliceFloatField []float64 + SlicesStructField StructSlices +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/zz_generated.defaults.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/zz_generated.defaults.go new file mode 100644 index 00000000000..26c7991bdec --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/output_tests/wholepkg/zz_generated.defaults.go @@ -0,0 +1,85 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package wholepkg + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&StructEverything{}, func(obj interface{}) { SetObjectDefaults_StructEverything(obj.(*StructEverything)) }) + scheme.AddTypeDefaultingFunc(&StructPointer{}, func(obj interface{}) { SetObjectDefaults_StructPointer(obj.(*StructPointer)) }) + scheme.AddTypeDefaultingFunc(&StructPrimitives{}, func(obj interface{}) { SetObjectDefaults_StructPrimitives(obj.(*StructPrimitives)) }) + scheme.AddTypeDefaultingFunc(&StructSlices{}, func(obj interface{}) { SetObjectDefaults_StructSlices(obj.(*StructSlices)) }) + scheme.AddTypeDefaultingFunc(&StructStructPrimitives{}, func(obj interface{}) { SetObjectDefaults_StructStructPrimitives(obj.(*StructStructPrimitives)) }) + return nil +} + +func SetObjectDefaults_StructEverything(in *StructEverything) { + SetObjectDefaults_StructPointer(&in.PointerStructField) + SetObjectDefaults_StructSlices(&in.SlicesStructField) +} + +func SetObjectDefaults_StructPointer(in *StructPointer) { + SetObjectDefaults_StructPrimitives(&in.PointerStructPrimitivesField) + if in.PointerPointerStructPrimitivesField != nil { + SetObjectDefaults_StructPrimitives(in.PointerPointerStructPrimitivesField) + } + SetObjectDefaults_StructStructPrimitives(&in.PointerStructStructPrimitives) + if in.PointerPointerStructStructPrimitives != nil { + SetObjectDefaults_StructStructPrimitives(in.PointerPointerStructStructPrimitives) + } +} + +func SetObjectDefaults_StructPrimitives(in *StructPrimitives) { + SetDefaults_StructPrimitives(in) +} + +func SetObjectDefaults_StructSlices(in *StructSlices) { + for i := range in.SliceStructPrimitivesField { + a := &in.SliceStructPrimitivesField[i] + SetObjectDefaults_StructPrimitives(a) + } + for i := range in.SlicePointerStructPrimitivesField { + a := in.SlicePointerStructPrimitivesField[i] + if a != nil { + SetObjectDefaults_StructPrimitives(a) + } + } + for i := range in.SliceStructStructPrimitives { + a := &in.SliceStructStructPrimitives[i] + SetObjectDefaults_StructStructPrimitives(a) + } + for i := range in.SlicePointerStructStructPrimitives { + a := in.SlicePointerStructStructPrimitives[i] + if a != nil { + SetObjectDefaults_StructStructPrimitives(a) + } + } +} + +func SetObjectDefaults_StructStructPrimitives(in *StructStructPrimitives) { + SetObjectDefaults_StructPrimitives(&in.StructField) +}