Commit 7cc3a6ea authored by Russell Cohen's avatar Russell Cohen
Browse files

Fix compilation error in generate code caused by name collision (#3175)

## Motivation and Context
If you had a model like this:
```smithy
   @http(uri: "/SomeOperation2", method: "GET")
            operation GetThing {
                // input: GetThingInput,
                output: GetThingOutput
            }
```

But then nested in some other API you did something like this:
```smithy
            list GetThings {
                member: GetThingOutput
            }
```

Code would fail to compile because we generated the same method
signature for two different types.

## Description
<!--- Describe your changes in detail -->

## Testing
- [x] fixes minimal reproducer

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
parent ccab4b69
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -6,7 +6,10 @@
package software.amazon.smithy.rust.codegen.client.smithy

import org.junit.jupiter.api.Test
import software.amazon.smithy.aws.traits.protocols.RestJson1Trait
import software.amazon.smithy.aws.traits.protocols.RestXmlTrait
import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest
import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.reusedInputOutputShapesModel
import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeEnumVariantsModel
import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeEnumsModel
import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeOperationsModel
@@ -32,4 +35,14 @@ class NamingObstacleCourseTest {
    fun `test Rust prelude enum variant names compile`() {
        clientIntegrationTest(rustPreludeEnumVariantsModel()) { _, _ -> }
    }

    @Test
    fun `test reuse of input and output shapes json`() {
        clientIntegrationTest(reusedInputOutputShapesModel(RestJson1Trait.builder().build()))
    }

    @Test
    fun `test reuse of input and output shapes xml`() {
        clientIntegrationTest(reusedInputOutputShapesModel(RestXmlTrait.builder().build()))
    }
}
+8 −1
Original line number Diff line number Diff line
@@ -18,6 +18,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
import software.amazon.smithy.rust.codegen.core.smithy.contextName
import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait
import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait
import software.amazon.smithy.rust.codegen.core.util.hasTrait
import software.amazon.smithy.rust.codegen.core.util.letIf
import software.amazon.smithy.rust.codegen.core.util.toSnakeCase

/**
@@ -139,10 +143,13 @@ internal fun RustSymbolProvider.shapeModuleName(serviceShape: ServiceShape?, sha

/** Creates a unique name for a ser/de function. */
fun RustSymbolProvider.shapeFunctionName(serviceShape: ServiceShape?, shape: Shape): String {
    val extras = "".letIf(shape.hasTrait<SyntheticOutputTrait>()) {
        it + "_output"
    }.letIf(shape.hasTrait<SyntheticInputTrait>()) { it + "_input" }
    val containerName = when (shape) {
        is MemberShape -> model.expectShape(shape.container).contextName(serviceShape).toSnakeCase()
        else -> shape.contextName(serviceShape).toSnakeCase()
    }
    } + extras
    return when (shape) {
        is MemberShape -> shape.memberName.toSnakeCase()
        is DocumentShape -> "document"
+58 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
package software.amazon.smithy.rust.codegen.core.testutil

import software.amazon.smithy.model.Model
import software.amazon.smithy.model.traits.Trait
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope

object NamingObstacleCourseTestModels {
@@ -169,4 +170,61 @@ object NamingObstacleCourseTestModels {
            """,
        )
    }.toString().asSmithyModel()

    /**
     * This targets two bug classes:
     * - operation inputs used as nested outputs
     * - operation outputs used as nested outputs
     */
    fun reusedInputOutputShapesModel(protocol: Trait) = """
        namespace test
        use ${protocol.toShapeId()}
        use aws.api#service
        @${protocol.toShapeId().name}
        @service(sdkId: "test")
        service Service {
            version: "2006-03-01",
            operations: [GetThing, ReuseGetThingIO]
        }

        // re-use get thing output in a list & in an operation
        @http(uri: "/SomeOperation2", method: "POST")
        operation GetThing {
            output: GetThingOutput
            input: GetThingInput
        }

        // an operation that re-uses the input and output shapes from `GetThing` above. this has caused issues in the
        // past with operation/input shape confusion during function signature generation
        @http(uri: "/SomeOperation3", method: "POST")
        operation ReuseGetThingIO {
            input: GetThingNested
            output: GetThingNested
        }

        structure GetThingOutput {
            @required
            meta: String
        }

        structure GetThingInput {
            @required
            meta: String
        }

        // nested structure which reuses input and output shapes internally
        structure GetThingNested {
            thingsOut: GetThingOutputList,
            thingsIn: GetThingInputList,
            thingOut: GetThingOutput,
            thingIn: GetThingInput
        }

        list GetThingOutputList {
            member: GetThingOutput
        }
        list GetThingInputList {
            member: GetThingInput
        }
    """.asSmithyModel()
}