Unverified Commit d626fd28 authored by Nate McMaster (AWS)'s avatar Nate McMaster (AWS) Committed by GitHub
Browse files

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 <https://github.com/rust-lang/rust/issues/79202

>

```

## Testing
Added a unit test to validate the sort order is applied correctly to
Attributes with isDeriveHelper = true.

---------

Co-authored-by: default avatardavid-perez <d@vidp.dev>
parent 8498ce84
Loading
Loading
Loading
Loading
+14 −3
Original line number Diff line number Diff line
@@ -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) {
+13 −0
Original line number Diff line number Diff line
@@ -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()