From 93e046722f600a0cb97934369873592899a1dea2 Mon Sep 17 00:00:00 2001 From: "Nate McMaster (AWS)" <88004173+mcmasn-amzn@users.noreply.github.com> Date: Wed, 19 Apr 2023 08:18:13 -0700 Subject: [PATCH] Fix for Rust compiler warning about derive helpers (#2581) ## Motivation and Context Follow-up to https://github.com/awslabs/smithy-rs/pull/2434 ## Description This provides a way to fix a compiler warning. Attributes created using RustMetadata.additionalAttributes may trigger compiler warnings (https://github.com/rust-lang/rust/issues/79202) such as ``` warning: derive helper attribute is used before it is introduced --> src/model.rs:7674:3 | 7674 | #[serde(tag = "_type", content = "_content")] | ^^^^^ 7675 | #[derive( 7676 | serde::Deserialize, | ------------------ the attribute is introduced here | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #79202 ``` ## Testing Added a unit test to validate the sort order is applied correctly to Attributes with isDeriveHelper = true. --------- Co-authored-by: david-perez --- .../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..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 @@ -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, see 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 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 is an attribute + * #[serde(serialize_with = "abc")] // <-- this attribute is a derive helper because the `Serialize` derive 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..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 @@ -133,6 +133,19 @@ class RustWriterTest { sut.toString().shouldContain("#[foo]\n/// here's an attribute") } + @Test + fun `attributes with derive helpers must come after derives`() { + 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()