Unverified Commit b9703dce authored by Landon James's avatar Landon James Committed by GitHub
Browse files

Fix bug with enum code generation (#4137)

parents 1b0f2a64 1d1cf7ff
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
---
applies_to: ["client"]
authors: ["landonxjames"]
references: ["smithy-rs#4137"]
breaking: false
new_feature: false
bug_fix: true
---

Fix bug with enum codegen

When the first enum generated has the `@sensitive` trait the opaque type
underlying the `UnknownVariant` inherits that sensitivity. This means that
it does not derive `Debug`. Since the module is only generated once this
causes a problem for non-sensitive enums that rely on the type deriving
`Debug` so that they can also derive `Debug`. We manually add `Debug` to
the module so it will always be there since the `UnknownVariant` is not
modeled and cannot be `@sensitive`.
+6 −1
Original line number Diff line number Diff line
@@ -194,7 +194,12 @@ data class InfallibleEnumType(
                This is not intended to be used directly.
                """.trimIndent(),
            )
            context.enumMeta.render(this)

            // The UnknownVariant's underlying opaque type should always derive `Debug`. If this isn't explicitly
            // added and the first enum to render this module has the `@sensitive` trait then the UnknownVariant
            // inherits that sensitivity and does not derive `Debug`. This leads to issues deriving `Debug` for
            // all enum variants that are not marked `@sensitive`.
            context.enumMeta.withDerives(RuntimeType.Debug).render(this)
            rustTemplate("struct $UNKNOWN_VARIANT_VALUE(pub(crate) #{String});", *preludeScope)
            rustBlock("impl $UNKNOWN_VARIANT_VALUE") {
                // The generated as_str is not pub as we need to prevent users from calling it on this opaque struct.
+49 −0
Original line number Diff line number Diff line
@@ -106,6 +106,55 @@ class ClientEnumGeneratorTest {
        project.compileAndTest()
    }

    // The idempotency of RustModules paired with Smithy's alphabetic order shape iteration meant that
    // if an @sensitive enum was the first enum generated that the opaque type underlying the Unknown
    // variant would not derive Debug, breaking all non-@sensitive enums
    @Test
    fun `sensitive enum in earlier alphabetic order does not break non-sensitive enums`() {
        val model =
            """
            namespace test

            @sensitive
            @enum([
                { name: "Foo", value: "Foo" },
                { name: "Bar", value: "Bar" },
            ])
            string FooA

            @enum([
                { name: "Baz", value: "Baz" },
                { name: "Ahh", value: "Ahh" },
            ])
            string FooB
            """.asSmithyModel()

        val shapeA = model.lookup<StringShape>("test#FooA")
        val shapeB = model.lookup<StringShape>("test#FooB")
        val context = testClientCodegenContext(model)
        val project = TestWorkspace.testProject(context.symbolProvider)
        project.moduleFor(shapeA) {
            ClientEnumGenerator(context, shapeA).render(this)
        }
        project.moduleFor(shapeB) {
            ClientEnumGenerator(context, shapeB).render(this)
            unitTest(
                "impl_debug_for_non_sensitive_enum_should_implement_the_derived_debug_trait",
                """
                assert_eq!(format!("{:?}", FooB::Baz), "Baz");
                assert_eq!(format!("{:?}", FooB::Ahh), "Ahh");
                assert_eq!(format!("{}", FooB::Baz), "Baz");
                assert_eq!(FooB::Ahh.to_string(), "Ahh");
                assert_eq!(
                    format!("{:?}", FooB::from("Bar")),
                    "Unknown(UnknownVariantValue(\"Bar\"))"
                );
                """,
            )
        }
        project.compileAndTest()
    }

    @Test
    fun `it escapes the Unknown variant if the enum has an unknown value in the model`() {
        val model =