Repository: aws-controllers-k8s/code-generator
Pull request #595: Allow zero and negative values for generated float32 bounds check
Author: login: knottnt
Labels: name: lgtm
description: Indicates that a PR is ready to be merged.
name: approved
Created: 2025-05-19T15:42:29Z | Merged: 2025-05-21T17:31:47Z

Issue #, if available:

Description of changes:
- Update `set_sdk.go setSDKForScalar()` to allow zero and negative values for float32 in bounds check

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Diff:
diff --git a/pkg/generate/code/set_sdk.go b/pkg/generate/code/set_sdk.go
index eae16d4a..6d792608 100644
--- a/pkg/generate/code/set_sdk.go
+++ b/pkg/generate/code/set_sdk.go
@@ -369,7 +369,6 @@ func SetSDK(
 					indentLevel+1,
 				)
 				out += setSDKForScalar(
-					cfg, r,
 					memberName,
 					targetVarName,
 					inputShape.Type,
@@ -391,7 +390,6 @@ func SetSDK(
 				)
 			} else {
 				out += setSDKForScalar(
-					cfg, r,
 					memberName,
 					targetVarName,
 					inputShape.Type,
@@ -577,7 +575,6 @@ func SetSDKGetAttributes(
 			indent, sourceVarPath,
 		)
 		out += setSDKForScalar(
-			cfg, r,
 			memberName,
 			targetVarName,
 			inputShape.Type,
@@ -788,7 +785,6 @@ func SetSDKSetAttributes(
 			indent, sourceVarPath,
 		)
 		out += setSDKForScalar(
-			cfg, r,
 			memberName,
 			targetVarName,
 			inputShape.Type,
@@ -918,7 +914,6 @@ func setSDKReadMany(
 
 			// res.SetIds(f0)
 			out += setSDKForScalar(
-				cfg, r,
 				memberName,
 				targetVarName,
 				inputShape.Type,
@@ -932,7 +927,6 @@ func setSDKReadMany(
 			// For ReadMany that have a singular identifier field.
 			// ex: DescribeReplicationGroups
 			out += setSDKForScalar(
-				cfg, r,
 				memberName,
 				targetVarName,
 				inputShape.Type,
@@ -1041,7 +1035,6 @@ func setSDKForContainer(
 		}
 
 		return setSDKForScalar(
-			cfg, r,
 			targetFieldName,
 			targetVarName,
 			targetShapeRef.Shape.Type,
@@ -1215,7 +1208,6 @@ func SetSDKForStruct(
 					indentLevel+1,
 				)
 				out += setSDKForScalar(
-					cfg, r,
 					memberName,
 					targetVarName,
 					targetShape.Type,
@@ -1238,7 +1230,6 @@ func SetSDKForStruct(
 			} else {
 
 				out += setSDKForScalar(
-					cfg, r,
 					memberName,
 					targetVarName,
 					targetShape.Type,
@@ -1558,8 +1549,6 @@ func varEmptyConstructorK8sType(
 // the aws-sdk-go's common SetXXX() method. For everything else, we output
 // normal assignment operations.
 func setSDKForScalar(
-	cfg *ackgenconfig.Config,
-	r *model.CRD,
 	// The name of the Input SDK Shape member we're outputting for
 	targetFieldName string,
 	// The variable name that we want to set a value to
@@ -1606,7 +1595,23 @@ func setSDKForScalar(
 		dereferencedVal := ogShapeName.CamelLower + "Copy0"
 		out += fmt.Sprintf("%s%s := %s\n", indent, dereferencedVal, setTo)
 
-		out += fmt.Sprintf("%sif %s > math.Max%s32 || %s < math.%s32 {\n", indent, dereferencedVal, actualType[0], dereferencedVal, actualType[1])
+		if shape.Type == "float" {
+			out += fmt.Sprintf(
+				"%[1]sif %[2]s > math.Max%[3]s32 || %[2]s < -math.Max%[3]s32 || (%[2]s < math.%[4]s32 && !(%[2]s <= 0)) || (%[2]s > -math.%[4]s32 && !(%[2]s >= 0)) {\n",
+				indent,
+				dereferencedVal,
+				actualType[0],
+				actualType[1],
+			)
+		} else {
+			out += fmt.Sprintf(
+				"%[1]sif %[2]s > math.Max%[3]s32 || %[2]s < math.%[4]s32 {\n",
+				indent,
+				dereferencedVal,
+				actualType[0],
+				actualType[1],
+			)
+		}
 		out += fmt.Sprintf("%s\treturn nil, fmt.Errorf(\"error: field %s is of type %s32\")\n", indent, ogShapeName.Original, actualType[2])
 		out += fmt.Sprintf("%s}\n", indent)
 		tempVar := ogShapeName.CamelLower + "Copy"
diff --git a/pkg/generate/code/set_sdk_whitebox_test.go b/pkg/generate/code/set_sdk_whitebox_test.go
new file mode 100644
index 00000000..d4915f3d
--- /dev/null
+++ b/pkg/generate/code/set_sdk_whitebox_test.go
@@ -0,0 +1,136 @@
+// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"). You may
+// not use this file except in compliance with the License. A copy of the
+// License is located at
+//
+//	 http://aws.amazon.com/apache2.0/
+//
+// or in the "license" file accompanying this file. This file 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 code
+
+import (
+	"testing"
+
+	awssdkmodel "github.com/aws-controllers-k8s/code-generator/pkg/api"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSetSDKForScalar(t *testing.T) {
+	assert := assert.New(t)
+
+	testCases := []struct {
+		name            string
+		targetFieldName string
+		targetVarName   string
+		targetVarType   string
+		sourceFieldPath string
+		sourceVarName   string
+		isListMember    bool
+		shapeRef        *awssdkmodel.ShapeRef
+		indentLevel     int
+		expected        string
+	}{
+		{
+			name:            "string scalar",
+			targetFieldName: "BucketName",
+			targetVarName:   "res",
+			targetVarType:   "structure",
+			sourceFieldPath: "Name",
+			sourceVarName:   "ko.Spec.Name",
+			isListMember:    false,
+			shapeRef: &awssdkmodel.ShapeRef{
+				Shape: &awssdkmodel.Shape{
+					Type: "string",
+				},
+				OriginalMemberName: "BucketName",
+			},
+			indentLevel: 1,
+			expected:    "\tres.BucketName = ko.Spec.Name\n",
+		},
+		{
+			name:            "boolean scalar",
+			targetFieldName: "Enabled",
+			targetVarName:   "res",
+			targetVarType:   "structure",
+			sourceFieldPath: "Enabled",
+			sourceVarName:   "ko.Spec.Enabled",
+			isListMember:    false,
+			shapeRef: &awssdkmodel.ShapeRef{
+				Shape: &awssdkmodel.Shape{
+					Type: "boolean",
+				},
+				OriginalMemberName: "Enabled",
+			},
+			indentLevel: 1,
+			expected:    "\tres.Enabled = ko.Spec.Enabled\n",
+		},
+		{
+			name:            "integer scalar",
+			targetFieldName: "MaxKeys",
+			targetVarName:   "res",
+			targetVarType:   "structure",
+			sourceFieldPath: "MaxKeys",
+			sourceVarName:   "ko.Spec.MaxKeys",
+			isListMember:    false,
+			shapeRef: &awssdkmodel.ShapeRef{
+				Shape: &awssdkmodel.Shape{
+					Type: "integer",
+				},
+				OriginalMemberName: "MaxKeys",
+			},
+			indentLevel: 1,
+			expected: `	maxKeysCopy0 := *ko.Spec.MaxKeys
+	if maxKeysCopy0 > math.MaxInt32 || maxKeysCopy0 < math.MinInt32 {
+		return nil, fmt.Errorf("error: field MaxKeys is of type int32")
+	}
+	maxKeysCopy := int32(maxKeysCopy0)
+	res.MaxKeys = &maxKeysCopy
+`,
+		},
+		{
+			name:            "float scalar",
+			targetFieldName: "Temperature",
+			targetVarName:   "res",
+			targetVarType:   "structure",
+			sourceFieldPath: "Temperature",
+			sourceVarName:   "ko.Spec.Temperature",
+			isListMember:    false,
+			shapeRef: &awssdkmodel.ShapeRef{
+				Shape: &awssdkmodel.Shape{
+					Type: "float",
+				},
+				OriginalMemberName: "Temperature",
+			},
+			indentLevel: 1,
+			expected: `	temperatureCopy0 := *ko.Spec.Temperature
+	if temperatureCopy0 > math.MaxFloat32 || temperatureCopy0 < -math.MaxFloat32 || (temperatureCopy0 < math.SmallestNonzeroFloat32 && !(temperatureCopy0 <= 0)) || (temperatureCopy0 > -math.SmallestNonzeroFloat32 && !(temperatureCopy0 >= 0)) {
+		return nil, fmt.Errorf("error: field Temperature is of type float32")
+	}
+	temperatureCopy := float32(temperatureCopy0)
+	res.Temperature = &temperatureCopy
+`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			result := setSDKForScalar(
+				tc.targetFieldName,
+				tc.targetVarName,
+				tc.targetVarType,
+				tc.sourceFieldPath,
+				tc.sourceVarName,
+				tc.isListMember,
+				tc.shapeRef,
+				tc.indentLevel,
+			)
+
+			assert.Equal(tc.expected, result, "setSDKForScalar() did not return expected result for %s", tc.name)
+		})
+	}
+}


Reviews:
user: login: michaelhtm
body: Great catch @knottnt!
left a few nits
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T17:45:42Z
user: login: knottnt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T18:21:29Z
user: login: michaelhtm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T18:49:45Z
user: login: michaelhtm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T18:50:38Z
user: login: a-hilaly
body: love it, thank Trevor! 🚀 
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-19T19:13:46Z
user: login: a-hilaly
body: 👍 
/lgtm
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-19T23:31:19Z
user: login: knottnt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T21:58:22Z
user: login: a-hilaly
body: 👍 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-21T17:30:33Z

Review comments:
diff_hunk: @@ -1575,7 +1575,17 @@ func setSDKForScalar(
 		dereferencedVal := ogShapeName.CamelLower + "Copy0"
 		out += fmt.Sprintf("%s%s := %s\n", indent, dereferencedVal, setTo)
 
-		out += fmt.Sprintf("%sif %s > math.Max%s32 || %s < math.%s32 {\n", indent, dereferencedVal, actualType[0], dereferencedVal, actualType[1])
+		if shape.Type == "float" {
+			out += fmt.Sprintf(
+				"%[1]sif %[2]s > math.Max%[3]s32 || %[2]s < -math.Max%[3]s32 || (%[2]s < math.%[4]s32 && !(%[2]s <= 0)) || (%[2]s > -math.%[4]s32 && !(%[2]s >= 0)) {\n",
path: pkg/generate/code/set_sdk.go
user: login: michaelhtm
body: This is a nice use! we should probably use it during the refactor!
created_at: 2025-05-19T17:44:00Z
author_association: MEMBER
diff_hunk: @@ -1575,7 +1575,17 @@ func setSDKForScalar(
 		dereferencedVal := ogShapeName.CamelLower + "Copy0"
 		out += fmt.Sprintf("%s%s := %s\n", indent, dereferencedVal, setTo)
 
-		out += fmt.Sprintf("%sif %s > math.Max%s32 || %s < math.%s32 {\n", indent, dereferencedVal, actualType[0], dereferencedVal, actualType[1])
+		if shape.Type == "float" {
+			out += fmt.Sprintf(
+				"%[1]sif %[2]s > math.Max%[3]s32 || %[2]s < -math.Max%[3]s32 || (%[2]s < math.%[4]s32 && !(%[2]s <= 0)) || (%[2]s > -math.%[4]s32 && !(%[2]s >= 0)) {\n",
+				indent,
+				dereferencedVal,
+				actualType[0],
+				actualType[1],
+			)
+		} else {
+			out += fmt.Sprintf("%sif %s > math.Max%s32 || %s < math.%s32 {\n", indent, dereferencedVal, actualType[0], dereferencedVal, actualType[1])
path: pkg/generate/code/set_sdk.go
user: login: michaelhtm
body: can we use the above here as well?
created_at: 2025-05-19T17:44:19Z
author_association: MEMBER
path: pkg/generate/code/set_sdk.go
user: login: michaelhtm
body: Can we modify the SmallestNonZeroFloat in `intOrFloat` instead? should be in L1564
created_at: 2025-05-19T17:45:22Z
author_association: MEMBER
path: pkg/generate/code/set_sdk.go
user: login: knottnt
body: Not totally sure what you mean by this. How would you like that list entry to be modified?
created_at: 2025-05-19T18:20:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1575,7 +1575,17 @@ func setSDKForScalar(
 		dereferencedVal := ogShapeName.CamelLower + "Copy0"
 		out += fmt.Sprintf("%s%s := %s\n", indent, dereferencedVal, setTo)
 
-		out += fmt.Sprintf("%sif %s > math.Max%s32 || %s < math.%s32 {\n", indent, dereferencedVal, actualType[0], dereferencedVal, actualType[1])
+		if shape.Type == "float" {
+			out += fmt.Sprintf(
+				"%[1]sif %[2]s > math.Max%[3]s32 || %[2]s < -math.Max%[3]s32 || (%[2]s < math.%[4]s32 && !(%[2]s <= 0)) || (%[2]s > -math.%[4]s32 && !(%[2]s >= 0)) {\n",
+				indent,
+				dereferencedVal,
+				actualType[0],
+				actualType[1],
+			)
+		} else {
+			out += fmt.Sprintf("%sif %s > math.Max%s32 || %s < math.%s32 {\n", indent, dereferencedVal, actualType[0], dereferencedVal, actualType[1])
path: pkg/generate/code/set_sdk.go
user: login: knottnt
body: Updated to use positional arguments.
created_at: 2025-05-19T18:20:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1575,7 +1575,17 @@ func setSDKForScalar(
 		dereferencedVal := ogShapeName.CamelLower + "Copy0"
 		out += fmt.Sprintf("%s%s := %s\n", indent, dereferencedVal, setTo)
 
-		out += fmt.Sprintf("%sif %s > math.Max%s32 || %s < math.%s32 {\n", indent, dereferencedVal, actualType[0], dereferencedVal, actualType[1])
+		if shape.Type == "float" {
+			out += fmt.Sprintf(
+				"%[1]sif %[2]s > math.Max%[3]s32 || %[2]s < -math.Max%[3]s32 || (%[2]s < math.%[4]s32 && !(%[2]s <= 0)) || (%[2]s > -math.%[4]s32 && !(%[2]s >= 0)) {\n",
path: pkg/generate/code/set_sdk.go
user: login: michaelhtm
body: One more nit here: can you show what the sample output of this line would be?
created_at: 2025-05-19T18:49:44Z
author_association: MEMBER
path: pkg/generate/code/set_sdk.go
user: login: michaelhtm
body: I meant 1591..I assumed you were no longer using SmallestNonZeroInt..
created_at: 2025-05-19T18:50:38Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,132 @@
+// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
path: pkg/generate/code/set_sdk_whitebox_test.go
user: login: knottnt
body: Had to use a new file since the existing `set_sdk_test.go` has a circular dependency with the code package due to `testutils`. 
created_at: 2025-05-20T21:58:22Z
author_association: CONTRIBUTOR

PR comments:
user: login: knottnt
created_at: 2025-05-19T20:49:49Z
body: /retest
author_association: CONTRIBUTOR
user: login: michaelhtm
created_at: 2025-05-19T21:20:18Z
body: /lgtm
/hold
author_association: MEMBER
user: login: knottnt
created_at: 2025-05-20T17:15:45Z
body: @michaelhtm @a-hilaly 

Example of the new output shown below. The new check is quite a bit larger. Assuming we want to prevent `float64` values smaller than `math.SmallestNonzeroFloat32` from being silently changed to zero we could reduce the size of the generated bounds check by adding a new function to the `runtime` package. Then in code-gen we simply call the new function. 
```go
if temperatureCopy0 > math.MaxFloat32 || temperatureCopy0 < -math.MaxFloat32 || (temperatureCopy0 < math.SmallestNonzeroFloat32 && !(temperatureCopy0 <= 0)) || (temperatureCopy0 > -math.SmallestNonzeroFloat32 && !(temperatureCopy0 >= 0)) {
	return nil, fmt.Errorf("error: field temperature is of type float32")
}
```

This is replacing the below check which does not allow zero or negative values.
```go
if temperatureCopy0 > math.MaxFloat32 || temperatureCopy0 < math.SmallestNonzeroFloat32 {
	return nil, fmt.Errorf("error: field temperature is of type float32")
}
```
author_association: CONTRIBUTOR
user: login: ack-prow[bot]
created_at: 2025-05-20T22:35:56Z
body: @knottnt: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
verify-attribution | ed4695dc279e306e7ae827fca761231ac38a006b | [link](https://prow.ack.aws.dev/view/s3/ack-prow-logs/pr-logs/pull/aws-controllers-k8s_code-generator/595/verify-attribution/1924956340199886848) | false | `/test verify-attribution`
s3-olm-test | ed4695dc279e306e7ae827fca761231ac38a006b | [link](https://prow.ack.aws.dev/view/s3/ack-prow-logs/pr-logs/pull/aws-controllers-k8s_code-generator/595/s3-olm-test/1924956340304744448) | false | `/test s3-olm-test`

[Full PR test history](https://prow.ack.aws.dev/pr-history?org=aws-controllers-k8s&repo=code-generator&pr=595). [Your PR dashboard](https://prow.ack.aws.dev/pr?query=is:pr+state:open+author:knottnt).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
author_association: NONE
user: login: knottnt
created_at: 2025-05-20T23:48:58Z
body: /test ec2-controller-test
author_association: CONTRIBUTOR
user: login: knottnt
created_at: 2025-05-21T15:36:49Z
body: /unhold
author_association: CONTRIBUTOR
user: login: a-hilaly
created_at: 2025-05-21T17:30:47Z
body: /lgtm
author_association: MEMBER
user: login: ack-prow[bot]
created_at: 2025-05-21T17:30:52Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/aws-controllers-k8s/code-generator/pull/595#issuecomment-2898723062" title="LGTM">a-hilaly</a>*, *<a href="https://github.com/aws-controllers-k8s/code-generator/pull/595#" title="Author self-approved">knottnt</a>*, *<a href="https://github.com/aws-controllers-k8s/code-generator/pull/595#issuecomment-2892287809" title="LGTM">michaelhtm</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=aws-controllers-k8s%2Fcode-generator).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/aws-controllers-k8s/code-generator/blob/main/OWNERS)~~ [a-hilaly,knottnt,michaelhtm]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: NONE