Skip to content

Conversation

vcjana
Copy link
Contributor

@vcjana vcjana commented Sep 24, 2025

Add test cases to expose unused variable warnings in union serialization

  • Tests create minimal Smithy models with unions containing unit structs
  • Use TestWorkspace pattern with project.compileAndTest() to validate generated code
  • Cover JSON, AWS Query, and CBOR serialization protocols
  • Tests designed to help validate serialization bug fixes

Motivation and Context

This change addresses the need for comprehensive test coverage to expose unused variable warnings in union serialization across different protocols. The tests are designed to validate the serialization bug fix and ensure that union members with
unit structs are handled correctly without generating unused variable warnings in the generated Rust code.

This work supports the ongoing effort to improve code generation quality and eliminate compiler warnings in generated SDK code.

Description

Added three new test cases across existing serializer test files to expose potential unused variable warnings in union serialization:

  1. JsonSerializerGeneratorTest.kt: Added union with unit struct demonstrates serialization bug() and union with unit struct demonstrates cbor serialization bug() tests
  2. AwsQuerySerializerGeneratorTest.kt: Added union with unit struct demonstrates query serialization bug() test

Each test:
• Creates a minimal Smithy model with a union containing both unit struct and data members
• Uses the TestWorkspace pattern with testSymbolProvider(model) and project.compileAndTest()
• Renders both the Unit structure and Union using renderWithModelBuilder() and UnionGenerator
• Validates that the generated Rust code compiles successfully
• Designed to expose unused variable warnings that would occur with the serialization bug

The tests follow the established patterns in the codebase and integrate seamlessly with existing test infrastructure.

Testing

• Local development environment with Gradle 8.14.3
• Kotlin compilation and test execution via ./gradlew :codegen-core:test

Checklist

  • For changes to the smithy-rs codegen or runtime crates, I have created a changelog entry Markdown file in the .changelog directory, specifying "client," "server," or both in the applies_to key.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

- Add test cases to expose unused variable warnings in union serialization
- Tests create minimal Smithy models with unions containing unit structs
- Use TestWorkspace pattern with project.compileAndTest() to validate generated code
- Cover JSON, AWS Query, and CBOR serialization protocols
- Tests designed to help validate serialization bug fixes
@vcjana vcjana requested review from a team as code owners September 24, 2025 18:24
Copy link
Contributor

@landonxjames landonxjames left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests look good, but the code fixing the issue seems to be missing?

}

@Test
fun `union with unit struct demonstrates query serialization bug`() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably change this to something like "Union with unit struct doesn't cause unused variable warning" or something along those lines, and maybe add a comment with a link to the issue so there is some context around what bug this is covering.


@Test
fun `union with unit struct demonstrates serialization bug`() {
val model =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are using the same model for both of these tests I would split it out to live outside of the tests so it isn't copied in multiple places.

But maybe the more salient point is I don't know that we need both of these tests? They look exactly the same to me besides the test names?


[[package]]
name = "sdk-lockfiles"
version = "0.1.3"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably rebase your branch from main since #4315 just merged so there aren't all these lockfile diffs

}

@Test
fun `union with unit struct demonstrates cbor serialization bug`() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh I see now, the CBOR test shouldn't be in this file, but there doesn't seem to be a CborSerializerGeneratorTest file. @ysaito1001 might know why that is missing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't realize till now. CborSerializerGenerator was originally bootstrapped by the server team's effort. After some time, client side support was added, and it's been assumed the generator was functional. We can certainly add a new test file CborSerializerGeneratorTest

Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

Copy link
Contributor

@ysaito1001 ysaito1001 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to make sure that the test crates generated by codegen tests issue the warning

unused variable: `inner`

without the fix (and shouldn't issue the warning WITH the fix).

When I ran one of the codegen tests, I didn't see the protocol_serde module rendered where the unused warning in question should be issued (occurrence in s3control for reference)

Could we double-check?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to add a test in QuerySerializerGeneratorTest instead of AwsQuerySerializerGeneratorTest

@vcjana vcjana force-pushed the union-serialization-test-fix branch from 57f8616 to 76c959c Compare September 24, 2025 23:01
Fixes unused variable warnings in union serialization for JSON, Query, and CBOR protocols.

Changes:
- JsonSerializerGenerator: Fix '_inner' -> 'inner' for non-unit structs
- QuerySerializerGenerator: Use '_inner' for unit structs, 'inner' for others
- CborSerializerGenerator: Fix '_inner' -> 'inner' for non-unit structs
- Add tests for JSON and Query serializers with protocol_serde generation
- Tests validate fix by generating actual union serialization code

Addresses issue #4308 by following the same pattern as the XML serializer fix.
Unit structs use '_inner' since the variable is never accessed in serialization.
@vcjana vcjana force-pushed the union-serialization-test-fix branch from 76c959c to 4cba820 Compare September 24, 2025 23:21
Copy link

A new generated diff is ready to view.

  • No codegen difference in the AWS SDK
  • No codegen difference in the Client Test
  • No codegen difference in the Server Test
  • No codegen difference in the Server Test Python
  • No codegen difference in the Server Test Typescript

A new doc preview is ready to view.

@ysaito1001
Copy link
Contributor

Good attempt at the latest commit 4cba820! We need to address the following:

  • union with unit struct doesn't cause unused variable warning in JsonSerializerGeneratorTest now renders the protocol_serde module, which is good, but it does not seem to issue unused variable warning (note that code fixing for JsonSerializerGenerator isn't commited to the PR yet, so the test should issue the unused warning if written as expected)? If we execute the test and inspect the generated test crate, the variable inner is used in blocks that immediately follow:
        crate::test_model::TestUnion::UnitMember(inner) => {
            #[allow(unused_mut)]
            let mut object_1 = object_2.key("unitMember").start_object();
            crate::protocol_serde::shape_unit::ser_unit(&mut object_1, inner)?;
            object_1.finish();
        }
        crate::test_model::TestUnion::DataMember(inner) => {
            object_2.key("dataMember").string(inner.as_str());
        }

So we probably need to iterate on the test model so that inner won't be used.

  • Now that union with unit struct doesn't cause unused variable warning in JsonSerializerGeneratorTest renders the protocol_serde module, the test is complained by the compiler because the rendered Rust constructs are unused in the test. We can refer to another test generates valid serializers, and see how project.lib {...} produces Rust code that does use serializer's code.

Copy link

A new generated diff is ready to view.

  • No codegen difference in the AWS SDK
  • No codegen difference in the Client Test
  • No codegen difference in the Server Test
  • No codegen difference in the Server Test Python
  • No codegen difference in the Server Test Typescript

A new doc preview is ready to view.

@vcjana vcjana force-pushed the union-serialization-test-fix branch from 456007b to 28da25e Compare September 25, 2025 18:11
Copy link

A new generated diff is ready to view.

  • No codegen difference in the AWS SDK
  • No codegen difference in the Client Test
  • No codegen difference in the Server Test
  • No codegen difference in the Server Test Python
  • No codegen difference in the Server Test Typescript

A new doc preview is ready to view.

@vcjana vcjana force-pushed the union-serialization-test-fix branch from 28da25e to 4cba820 Compare September 25, 2025 18:42
Copy link

A new generated diff is ready to view.

  • No codegen difference in the AWS SDK
  • No codegen difference in the Client Test
  • No codegen difference in the Server Test
  • No codegen difference in the Server Test Python
  • No codegen difference in the Server Test Typescript

A new doc preview is ready to view.

Copy link

A new generated diff is ready to view.

  • No codegen difference in the AWS SDK
  • No codegen difference in the Client Test
  • No codegen difference in the Server Test
  • No codegen difference in the Server Test Python
  • No codegen difference in the Server Test Typescript

A new doc preview is ready to view.

Copy link

A new generated diff is ready to view.

  • No codegen difference in the AWS SDK
  • No codegen difference in the Client Test
  • No codegen difference in the Server Test
  • No codegen difference in the Server Test Python
  • No codegen difference in the Server Test Typescript

A new doc preview is ready to view.

@smithy-lang smithy-lang deleted a comment from github-actions bot Sep 25, 2025
@vcjana vcjana force-pushed the union-serialization-test-fix branch from 49c87c6 to 23f415e Compare September 25, 2025 21:22
…issue

- Remove unused TestInput imports and fix structure references
- Perfect test model to only include unit struct member to demonstrate unused variable issue
- Ensure tests properly use serializer code to avoid dead code warnings
- Update test model structure names to match generated code expectations
@vcjana vcjana force-pushed the union-serialization-test-fix branch 2 times, most recently from 5259ca2 to d71bd1b Compare September 25, 2025 22:33
Copy link

A new generated diff is ready to view.

  • AWS SDK (ignoring whitespace)
  • No codegen difference in the Client Test
  • No codegen difference in the Server Test
  • No codegen difference in the Server Test Python
  • No codegen difference in the Server Test Typescript

A new doc preview is ready to view.

Copy link
Collaborator

@rcoh rcoh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you validate that your test fails without these changes?

if (member.isTargetUnit()) {
"${symbolProvider.toMemberName(member)}"
} else if (memberShape.isStructureShape &&
memberShape.asStructureShape().get().allMembers.isEmpty()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description implies this is a test-only PR. Is this intentional?

let _serialized = ${format(operationGenerator!!)};
let _result = _serialized(&input);
// Test that the code compiles and runs - this validates our fix works
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete ai comment

Copy link

A new generated diff is ready to view.

  • AWS SDK (ignoring whitespace)
  • No codegen difference in the Client Test
  • No codegen difference in the Server Test
  • No codegen difference in the Server Test Python
  • No codegen difference in the Server Test Typescript

A new doc preview is ready to view.

@vcjana vcjana force-pushed the union-serialization-test-fix branch 2 times, most recently from 9140ee0 to 3538d39 Compare September 26, 2025 22:03
…ctures

- Use _inner only for empty structures, inner for structures with data
- Add comprehensive tests with RUSTFLAGS to verify unused variable handling
- Complete fix for issue #4308 across JSON, CBOR, and Query protocols
@vcjana vcjana force-pushed the union-serialization-test-fix branch from 3538d39 to 2a71c21 Compare September 26, 2025 22:36
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

landonxjames
landonxjames previously approved these changes Sep 27, 2025
Copy link
Contributor

@landonxjames landonxjames left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple more comments, looks like it is coming along. Lets get together early next week to iron out some things

}
rustBlock("#T::$variantName =>", unionSymbol) {
serializeMember(MemberContext.unionMember("inner", member))
val innerRef = if (isEmptyStruct) "_inner" else "inner"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary? I think that for the isEmptyStruct case it doesn't really matter what we pass to serializeMember because there are no members to serialize.

This comment applies to all of the generators since it looks like there is similar code in all of the updates.

fun `union with unit struct doesn't cause unused variable warning`() {
// Regression test for https://github.com/smithy-lang/smithy-rs/issues/4308
// This test ensures that union serialization with unit structs compiles without unused variable warnings.
val model = RecursiveShapeBoxer().transform(OperationNormalizer.transform(QuerySerializerGeneratorTest.unionWithUnitStructModel))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things here:

  • I don't think some of these transforms are needed, at least the RecursiveShapeBoxer, since the model being tested doesn't have any recursive shapes.
  • Feels kind of weird importing the model from the QuerySerializerGeneratorTest. I would either split that out into a shared SerializerGeneratorTestUtils class or just copy it into each test, either is fine.

}

// The test passes if the generated code compiles without unused variable warnings
project.compileAndTest()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure the comment above is true. In the lines below we disable failing on warnings for compileAndTest:

// Clean `RUSTFLAGS` because in CI we pass in `--deny warnings` and
// we still generate test code with warnings.
// TODO(https://github.com/smithy-lang/smithy-rs/issues/3194)
val env = mapOf("RUSTFLAGS" to "")

If the generated code here is relatively clean we could probably add an option to compileAndTest that allows failing on warnings, but if there are a bunch of warnings beyond the one we are trying to fix we might need a different approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants