Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,50 @@ class AwsQueryTest {
}
""".asSmithyModel()

private val inputUnionWithEmptyStructure =
"""
namespace test
use aws.protocols#awsQuery

@awsQuery
@xmlNamespace(uri: "https://example.com/")
service TestService {
version: "2019-12-16",
operations: [TestOp]
}

operation TestOp {
input: TestInput
}

structure TestInput {
testUnion: TestUnion
}

union TestUnion {
// Empty struct - should generate _inner to avoid unused variable warning
emptyStruct: EmptyStruct,

// Normal struct - should generate inner (without underscore)
normalStruct: NormalStruct
}

structure EmptyStruct {}

structure NormalStruct {
value: String
}
""".asSmithyModel()

@Test
fun `generate an aws query service that compiles`() {
clientIntegrationTest(model) { _, _ -> }
}

@Test
fun `union with empty struct generates warning-free code`() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Good test name 👍

// This test will fail with unused variable warnings if the fix is not applied
// clientIntegrationTest enforces -D warnings via codegenIntegrationTest
clientIntegrationTest(inputUnionWithEmptyStructure) { _, _ -> }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,54 @@ internal class RestJsonTest {
}
""".asSmithyModel()

private val inputUnionWithEmptyStructure =
"""
namespace test
use aws.protocols#restJson1
use aws.api#service

@service(sdkId: "Rest Json Empty Struct")
@restJson1
service RestJsonEmptyStruct {
version: "2019-12-16",
operations: [TestOp]
}

@http(uri: "/test", method: "POST")
operation TestOp {
input: TestInput
}

structure TestInput {
testUnion: TestUnion
}

union TestUnion {
// Empty struct - RestJson ALWAYS uses inner variable, no warning
emptyStruct: EmptyStruct,

// Normal struct - RestJson uses inner variable
normalStruct: NormalStruct
}

structure EmptyStruct {}

structure NormalStruct {
value: String
}
""".asSmithyModel()

@Test
fun `generate a rest json service that compiles`() {
clientIntegrationTest(model) { _, _ -> }
}

@Test
fun `union with empty struct always uses inner variable`() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Good test name 👍

// This test documents that RestJson protocol is immune to unused variable issues.
// Unlike RestXml/AwsQuery, RestJson serializers always reference the inner variable
// even for empty structs, so no underscore prefix is needed.
// This test passes without any code changes, proving RestJson immunity.
clientIntegrationTest(inputUnionWithEmptyStructure) { _, _ -> }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,51 @@ internal class RestXmlTest {

""".asSmithyModel()

private val inputUnionWithEmptyStructure =
"""
namespace test
use aws.protocols#restXml
use aws.api#service

@service(sdkId: "Rest XML Empty Struct")
@restXml
service RestXmlEmptyStruct {
version: "2019-12-16",
operations: [TestOp]
}

@http(uri: "/test", method: "POST")
operation TestOp {
input: TestInput
}

structure TestInput {
testUnion: TestUnion
}

union TestUnion {
// Empty struct - should generate _inner to avoid unused variable warning
emptyStruct: EmptyStruct,
// Normal struct - should generate inner (without underscore)
normalStruct: NormalStruct
}

structure EmptyStruct {}

structure NormalStruct {
value: String
}
""".asSmithyModel()

@Test
fun `generate a rest xml service that compiles`() {
clientIntegrationTest(model) { _, _ -> }
}

@Test
fun `union with empty struct generates warning-free code`() {
// This test will fail with unused variable warnings if the fix is not applied
// clientIntegrationTest enforces -D warnings via codegenIntegrationTest
clientIntegrationTest(inputUnionWithEmptyStructure) { _, _ -> }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -345,29 +345,51 @@ abstract class QuerySerializerGenerator(private val codegenContext: CodegenConte
}
}

/**
* Determines if a struct shape is empty (has no members).
* Empty structs result in unused variables in union match arms since the inner value is never referenced.
*/
private fun isEmptyStruct(shape: Shape): Boolean =
when (shape) {
is StructureShape -> shape.members().isEmpty()
else -> false
}

private fun RustWriter.serializeUnion(context: Context<UnionShape>) {
val unionSymbol = symbolProvider.toSymbol(context.shape)

// Check if any union member uses the writer (non-empty structs)
val hasNonEmptyMember =
context.shape.members().any { member ->
!member.isTargetUnit() && !isEmptyStruct(model.expectShape(member.target))
}
val writerVarName = if (hasNonEmptyMember) "writer" else "_writer"

val unionSerializer =
protocolFunctions.serializeFn(context.shape) { fnName ->
Attribute.AllowUnusedMut.render(this)
rustBlockTemplate(
"pub fn $fnName(mut writer: #{QueryValueWriter}, input: &#{Input}) -> #{Result}<(), #{Error}>",
"pub fn $fnName(mut $writerVarName: #{QueryValueWriter}, input: &#{Input}) -> #{Result}<(), #{Error}>",
"Input" to unionSymbol,
*codegenScope,
) {
rustBlock("match input") {
for (member in context.shape.members()) {
val targetShape = model.expectShape(member.target)
// Use underscore prefix for empty structs to avoid unused variable warnings
val innerVarName = if (isEmptyStruct(targetShape)) "_inner" else "inner"

val variantName =
if (member.isTargetUnit()) {
"${symbolProvider.toMemberName(member)}"
} else {
"${symbolProvider.toMemberName(member)}(inner)"
"${symbolProvider.toMemberName(member)}($innerVarName)"
}
withBlock("#T::$variantName => {", "},", unionSymbol) {
serializeMember(
MemberContext.unionMember(
context.copy(writerExpression = "writer"),
"inner",
context.copy(writerExpression = writerVarName),
innerVarName,
member,
),
)
Expand Down
Loading
Loading