From 0aa4b88623ff750c01d41d6f27c87133d3e86b07 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Fri, 14 Apr 2023 08:36:11 -0700 Subject: [PATCH 1/2] Add option to mark attributes as derive helpers --- .../rust/codegen/core/rustlang/RustType.kt | 17 ++++++++++++++--- .../codegen/core/rustlang/RustWriterTest.kt | 13 +++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index e4d27fe451..7c9bef6eda 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -385,11 +385,17 @@ data class RustMetadata( this.copy(derives = derives - withoutDerives.toSet()) fun renderAttributes(writer: RustWriter): RustMetadata { - additionalAttributes.forEach { + val (deriveHelperAttrs, otherAttrs) = additionalAttributes.partition { it.isDeriveHelper } + otherAttrs.forEach { it.render(writer) } + Attribute(derive(derives)).render(writer) + // derive helper attributes must come after derive -- https://github.com/rust-lang/rust/issues/79202 + deriveHelperAttrs.forEach { + it.render(writer) + } return this } @@ -450,17 +456,22 @@ enum class AttributeKind { * [Attributes](https://doc.rust-lang.org/reference/attributes.html) are general free form metadata * that are interpreted by the compiler. * + * If the attribute is a 'derive helper', such #[serde], set isDeriveHelper to true so it is sorted correctly after + * the derive attribute is rendered. (See https://github.com/rust-lang/rust/issues/79202 for why sorting matters.) + * * For example: * ```rust + * #[allow(missing_docs)] // <-- this is an attribute, and it is not a derive helper * #[derive(Clone, PartialEq, Serialize)] // <-- this is an attribute - * #[serde(serialize_with = "abc")] // <-- this is an attribute + * #[serde(serialize_with = "abc")] // <-- this attribute is a derive helper because the Serialize macro uses it * struct Abc { * a: i64 * } * ``` */ -class Attribute(val inner: Writable) { +class Attribute(val inner: Writable, val isDeriveHelper: Boolean = false) { constructor(str: String) : this(writable(str)) + constructor(str: String, isDeriveHelper: Boolean) : this(writable(str), isDeriveHelper) constructor(runtimeType: RuntimeType) : this(runtimeType.writable) fun render(writer: RustWriter, attributeKind: AttributeKind = AttributeKind.Outer) { diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt index 3b0ef7c557..c14b2ed356 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt @@ -133,6 +133,19 @@ class RustWriterTest { sut.toString().shouldContain("#[foo]\n/// here's an attribute") } + @Test + fun `attributes with derive helpers`() { + val attr = Attribute("foo", isDeriveHelper = true) + val metadata = RustMetadata( + derives = setOf(RuntimeType.Debug), + additionalAttributes = listOf(Attribute.AllowDeprecated, attr), + visibility = Visibility.PUBLIC, + ) + val sut = RustWriter.root() + metadata.render(sut) + sut.toString().shouldContain("#[allow(deprecated)]\n#[derive(std::fmt::Debug)]\n#[foo]") + } + @Test fun `deprecated attribute without any field`() { val sut = RustWriter.root() From a7cb2b49924a6826063155cfba3b42c2774f9ca1 Mon Sep 17 00:00:00 2001 From: "Nate McMaster (AWS)" <88004173+mcmasn-amzn@users.noreply.github.com> Date: Mon, 17 Apr 2023 08:25:24 -0700 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: david-perez --- .../amazon/smithy/rust/codegen/core/rustlang/RustType.kt | 6 +++--- .../smithy/rust/codegen/core/rustlang/RustWriterTest.kt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index 7c9bef6eda..56f0b63c52 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -392,7 +392,7 @@ data class RustMetadata( Attribute(derive(derives)).render(writer) - // derive helper attributes must come after derive -- https://github.com/rust-lang/rust/issues/79202 + // Derive helper attributes must come after derive, see https://github.com/rust-lang/rust/issues/79202 deriveHelperAttrs.forEach { it.render(writer) } @@ -456,14 +456,14 @@ enum class AttributeKind { * [Attributes](https://doc.rust-lang.org/reference/attributes.html) are general free form metadata * that are interpreted by the compiler. * - * If the attribute is a 'derive helper', such #[serde], set isDeriveHelper to true so it is sorted correctly after + * If the attribute is a "derive helper", such as `#[serde]`, set `isDeriveHelper` to `true` so it is sorted correctly after * the derive attribute is rendered. (See https://github.com/rust-lang/rust/issues/79202 for why sorting matters.) * * For example: * ```rust * #[allow(missing_docs)] // <-- this is an attribute, and it is not a derive helper * #[derive(Clone, PartialEq, Serialize)] // <-- this is an attribute - * #[serde(serialize_with = "abc")] // <-- this attribute is a derive helper because the Serialize macro uses it + * #[serde(serialize_with = "abc")] // <-- this attribute is a derive helper because the `Serialize` derive uses it * struct Abc { * a: i64 * } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt index c14b2ed356..ac14bcd20f 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt @@ -134,7 +134,7 @@ class RustWriterTest { } @Test - fun `attributes with derive helpers`() { + fun `attributes with derive helpers must come after derives`() { val attr = Attribute("foo", isDeriveHelper = true) val metadata = RustMetadata( derives = setOf(RuntimeType.Debug),