Unverified Commit e38db43c authored by Weihang Lo's avatar Weihang Lo Committed by GitHub
Browse files

Basic support of @deprecated trait in Smithy model (#1570)



* Add helper for creating deprecated attribute

* `Attribute.Custom.deprecated` is the main logic for building up
  `#[deprecated]` attribute
* `RustWriter.deprecatedShape` is the counterpart of `documentShape`,
  but we do not going to generalize it as what `documentShape` does.
  Deprecated is only for Rust code and probably won't be used in other
  output language.

Signed-off-by: default avatarWeihang Lo <whlo@amazon.co.uk>

* Test `@deprecated` trait for RustWriter

* Support `@deprecated` trait for StructureGenerator

* Support `@deprecated` trait for UnionGenerator

Signed-off-by: default avatarWeihang Lo <whlo@amazon.co.uk>

* Support `@deprecated` trait for EnumGenerator

* Support `@deprecated` trait for TopLevelErrorGenerator

* Support `@deprecated` trait for CombinedErrorGenerator

* Support `@deprecated` trait for ServerCombinedErrorGenerator

* Support `@deprecated` trait for FluentClient

* Support `@deprecated` trait for BuilderGenerator

* Cleanup leftover in test

* Use `dq()` helper method instead of escaping by hands

Signed-off-by: default avatarWeihang Lo <whlo@amazon.co.uk>

* Leverage Kotlin null safety well

Signed-off-by: default avatarWeihang Lo <whlo@amazon.co.uk>

* Allow `deprecated` rustc lint rule

Signed-off-by: default avatarWeihang Lo <whlo@amazon.co.uk>

* Allow deprecated in unit tests

Signed-off-by: default avatarWeihang Lo <whlo@amazon.co.uk>

* Leverage kotlin null safety check again

Signed-off-by: default avatarWeihang Lo <whlo@amazon.co.uk>

* changelog: Support @deprecated trait

Signed-off-by: default avatarWeihang Lo <whlo@amazon.co.uk>

Co-authored-by: default avatarMatteo Bigoi <1781140+crisidev@users.noreply.github.com>
parent 74a106f1
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -219,3 +219,9 @@ message = "Change detailed logs in CredentialsProviderChain from info to debug"
references = ["smithy-rs#1578"]
meta = { "breaking" = false, "tada" = false, "bug" = false }
author = "lkts"

[[smithy-rs]]
message = "Support @deprecated trait for aggregate shapes"
references = ["smithy-rs#1570"]
meta = { "breaking" = true, "tada" = true, "bug" = false }
author = "weihanglo"
+10 −3
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ class ServerCombinedErrorGeneratorTest {
        namespace error

        operation Greeting {
            errors: [InvalidGreeting, ComplexError, FooException]
            errors: [InvalidGreeting, ComplexError, FooException, Deprecated]
        }

        @error("client")
@@ -42,6 +42,10 @@ class ServerCombinedErrorGeneratorTest {
            abc: String,
            other: Integer
        }

        @error("server")
        @deprecated
        structure Deprecated { }
    """.asSmithyModel()
    private val model = OperationNormalizer.transform(baseModel)
    private val symbolProvider = serverTestSymbolProvider(model)
@@ -50,7 +54,7 @@ class ServerCombinedErrorGeneratorTest {
    fun `generates combined error enums`() {
        val project = TestWorkspace.testProject(symbolProvider)
        project.withModule(RustModule.public("error")) { writer ->
            listOf("FooException", "ComplexError", "InvalidGreeting").forEach {
            listOf("FooException", "ComplexError", "InvalidGreeting", "Deprecated").forEach {
                model.lookup<StructureShape>("error#$it").renderWithModelBuilder(model, symbolProvider, writer, CodegenTarget.SERVER)
            }
            val errors = listOf("FooException", "ComplexError", "InvalidGreeting").map { model.lookup<StructureShape>("error#$it") }
@@ -76,7 +80,10 @@ class ServerCombinedErrorGeneratorTest {

                    // Indicate the original name in the display output.
                    let error = FooException::builder().build();
                    assert_eq!(format!("{}", error), "FooException")
                    assert_eq!(format!("{}", error), "FooException");

                    let error = Deprecated::builder().build();
                    assert_eq!(error.to_string(), "Deprecated");
                """,
            )

+23 −0
Original line number Diff line number Diff line
@@ -374,6 +374,29 @@ sealed class Attribute {
                writer.addDependency(it.dependency)
            }
        }

        companion object {
            /**
             * Renders a
             * [`#[deprecated]`](https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute)
             * attribute.
             */
            fun deprecated(note: String? = null, since: String? = null): Custom {
                val builder = StringBuilder()
                builder.append("deprecated")

                if (note != null && since != null) {
                    builder.append("(note = ${note.dq()}, since = ${since.dq()})")
                } else if (note != null) {
                    builder.append("(note = ${note.dq()})")
                } else if (since != null) {
                    builder.append("(since = ${since.dq()})")
                } else {
                    // No-op. Rustc would emit a default message.
                }
                return Custom(builder.toString())
            }
        }
    }

    data class Cfg(val cond: String) : Attribute() {
+16 −0
Original line number Diff line number Diff line
@@ -18,10 +18,12 @@ import software.amazon.smithy.model.shapes.CollectionShape
import software.amazon.smithy.model.shapes.NumberShape
import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.model.traits.DeprecatedTrait
import software.amazon.smithy.model.traits.DocumentationTrait
import software.amazon.smithy.rust.codegen.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.smithy.isOptional
import software.amazon.smithy.rust.codegen.smithy.rustType
import software.amazon.smithy.rust.codegen.util.getTrait
import software.amazon.smithy.rust.codegen.util.orNull
import software.amazon.smithy.utils.AbstractCodeWriter
import java.io.File
@@ -269,6 +271,20 @@ fun <T : AbstractCodeWriter<T>> T.docs(text: String, vararg args: Any, newlinePr
    return this
}

/**
 * Generates a `#[deprecated]` attribute for [shape].
 */
fun RustWriter.deprecatedShape(shape: Shape): RustWriter {
    val deprecatedTrait = shape.getTrait<DeprecatedTrait>() ?: return this

    val note = deprecatedTrait.message.orNull()
    val since = deprecatedTrait.since.orNull()

    Attribute.Custom.deprecated(note, since).render(this)

    return this
}

/** Escape the [expressionStart] character to avoid problems during formatting */
fun <T : AbstractCodeWriter<T>> T.escape(text: String): String =
    text.replace("$expressionStart", "$expressionStart$expressionStart")
+12 −7
Original line number Diff line number Diff line
@@ -10,7 +10,12 @@ import software.amazon.smithy.rust.codegen.rustlang.writable
import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization
import software.amazon.smithy.rust.codegen.smithy.generators.LibRsSection

val ClippyAllowLints = listOf(
val AllowedRustcLints = listOf(
    // Deprecated items should be safe to compile, so don't block the compilation.
    "deprecated",
)

val AllowedClippyLints = listOf(
    // Sometimes operations are named the same as our module e.g. output leading to `output::output`.
    "module_inception",

@@ -36,26 +41,26 @@ val ClippyAllowLints = listOf(
    "type_complexity",
)

val AllowDocsLints = listOf(
val AllowedRustdocLints = listOf(
    // Rust >=1.53.0 requires links to be wrapped in `<link>`. This is extremely hard to enforce for
    // docs that come from the modeled documentation, so we need to disable this lint
    "bare_urls",
)

class AllowLintsGenerator(
    private val bareLints: List<String> = listOf(),
    private val clippyLints: List<String> = ClippyAllowLints,
    private val docsLints: List<String> = AllowDocsLints,
    private val rustcLints: List<String> = AllowedRustcLints,
    private val clippyLints: List<String> = AllowedClippyLints,
    private val rustdocLints: List<String> = AllowedRustdocLints,
) : LibRsCustomization() {
    override fun section(section: LibRsSection) = when (section) {
        is LibRsSection.Attributes -> writable {
            bareLints.forEach {
            rustcLints.forEach {
                Attribute.Custom("allow($it)", container = true).render(this)
            }
            clippyLints.forEach {
                Attribute.Custom("allow(clippy::$it)", container = true).render(this)
            }
            docsLints.forEach {
            rustdocLints.forEach {
                Attribute.Custom("allow(rustdoc::$it)", container = true).render(this)
            }
            // add a newline at the end
Loading