Skip to content

Commit eaa4518

Browse files
authored
fix: codegen structure members more safely to avoid conflicts with package names (#1444)
1 parent 9db4065 commit eaa4518

File tree

4 files changed

+49
-8
lines changed

4 files changed

+49
-8
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"id": "69d72d7f-7266-4d90-81d2-29aac4821386",
3+
"type": "bugfix",
4+
"description": "fix: code-generate structure members more safely to avoid conflicts with package names"
5+
}

codegen/smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/StructureGenerator.kt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import software.amazon.smithy.kotlin.codegen.core.withBlock
1313
import software.amazon.smithy.kotlin.codegen.lang.KotlinTypes
1414
import software.amazon.smithy.kotlin.codegen.model.*
1515
import software.amazon.smithy.kotlin.codegen.rendering.serde.ClientErrorCorrection
16+
import software.amazon.smithy.kotlin.codegen.utils.toCamelCase
1617
import software.amazon.smithy.model.Model
1718
import software.amazon.smithy.model.shapes.*
1819
import software.amazon.smithy.model.traits.*
@@ -32,6 +33,7 @@ class StructureGenerator(
3233
private val symbol = ctx.symbolProvider.toSymbol(ctx.shape)
3334

3435
fun render() {
36+
renderDslBuilderRefs()
3537
writer.renderDocumentation(shape)
3638
writer.renderAnnotations(shape)
3739
if (!shape.isError) {
@@ -42,11 +44,14 @@ class StructureGenerator(
4244
}
4345

4446
private val sortedMembers: List<MemberShape> = shape.allMembers.values.sortedBy { it.defaultName() }
47+
4548
private val memberNameSymbolIndex: Map<MemberShape, Pair<String, Symbol>> =
4649
sortedMembers.associateWith { member ->
4750
Pair(symbolProvider.toMemberName(member), symbolProvider.toSymbol(member))
4851
}
4952

53+
private val structMembers = sortedMembers.filter { model.expectShape(it.target).isStructureShape }
54+
5055
/**
5156
* Renders a normal (non-error) Smithy structure to a Kotlin class
5257
*/
@@ -283,18 +288,13 @@ class StructureGenerator(
283288
write("@PublishedApi")
284289
write("internal fun build(): #1Q = #1T(this)", symbol)
285290

286-
val structMembers = sortedMembers.filter { member ->
287-
val targetShape = model.getShape(member.target).get()
288-
targetShape.isStructureShape
289-
}
290-
291291
for (member in structMembers) {
292292
writer.write("")
293293
val (memberName, memberSymbol) = memberNameSymbolIndex[member]!!
294294
writer.dokka("construct an [${memberSymbol.fullName}] inside the given [block]")
295295
writer.renderAnnotations(member)
296296
openBlock("public fun #L(block: #Q.Builder.() -> #Q) {", memberName, memberSymbol, KotlinTypes.Unit)
297-
.write("this.#L = #Q.invoke(block)", memberName, memberSymbol)
297+
.write("this.#L = #L(block)", memberName, dslBuilderRef(memberSymbol))
298298
.closeBlock("}")
299299
}
300300

@@ -324,6 +324,21 @@ class StructureGenerator(
324324
}
325325
}
326326

327+
/**
328+
* Derives a hopefully collision-safe name for a symbol by camel-casing the full name and "DSL Builder Ref". For
329+
* instance, the symbol `aws.sdk.kotlin.services.simplefooservice.model.FooBar` turns into
330+
* `awsSdkKotlinServicesSimplefooserviceModelFooBarDslBuilderRef`.
331+
*/
332+
private fun dslBuilderRef(symbol: Symbol): String = "${symbol.fullName} DSL Builder Ref".toCamelCase()
333+
334+
private fun renderDslBuilderRefs() {
335+
val dslBuilderSymbols = structMembers.map { memberNameSymbolIndex[it]!!.second }.toSet()
336+
dslBuilderSymbols.forEach { symbol ->
337+
writer.write("private val #L = #Q", dslBuilderRef(symbol), symbol)
338+
}
339+
writer.write("")
340+
}
341+
327342
/**
328343
* Renders a Smithy error type to a Kotlin exception type
329344
*/

codegen/smithy-kotlin-codegen/src/test/kotlin/software/amazon/smithy/kotlin/codegen/rendering/StructureGeneratorTest.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ class StructureGeneratorTest {
221221
* construct an [com.test.model.Qux] inside the given [block]
222222
*/
223223
public fun quux(block: com.test.model.Qux.Builder.() -> kotlin.Unit) {
224-
this.quux = com.test.model.Qux.invoke(block)
224+
this.quux = comTestModelQuxDslBuilderRef(block)
225225
}
226226
227227
internal fun correctErrors(): Builder {
@@ -233,6 +233,11 @@ class StructureGeneratorTest {
233233
commonTestContents.shouldContainOnlyOnceWithDiff(expected)
234234
}
235235

236+
@Test
237+
fun `it renders DSL builder references`() {
238+
commonTestContents.shouldContainOnlyOnceWithDiff("private val comTestModelQuxDslBuilderRef = com.test.model.Qux")
239+
}
240+
236241
@Test
237242
fun `it renders class docs`() {
238243
commonTestContents.shouldContainOnlyOnceWithDiff("This *is* documentation about the shape.")

tests/compile/src/test/resources/kitchen-sink-model.smithy

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ service Example {
2626
UnionAggregateInput,
2727
UnionOutput,
2828
UnionAggregateOutput,
29-
WaiterTest
29+
WaiterTest,
30+
PackageNameConflictTest,
3031
]
3132
}
3233

@@ -536,3 +537,18 @@ structure WaiterTestFoo {
536537

537538
@error("client")
538539
structure WaiterTestNotFound {}
540+
541+
// Verifies that members with the same name as a top-level package don't cause compilation issues.
542+
@http(method: "POST", uri: "/input/packageNameConflict")
543+
operation PackageNameConflictTest {
544+
input: PackageNameConflictTestRequest,
545+
output: PackageNameConflictTestResponse,
546+
}
547+
548+
structure PackageNameConflictTestRequest {
549+
com: String,
550+
}
551+
552+
structure PackageNameConflictTestResponse {
553+
com: String,
554+
}

0 commit comments

Comments
 (0)