diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index a1db53d77d..755d7722fd 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -218,4 +218,10 @@ author = "crisidev" message = "Change detailed logs in CredentialsProviderChain from info to debug" references = ["smithy-rs#1578"] meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "lkts" \ No newline at end of file +author = "lkts" + +[[smithy-rs]] +message = "Support @deprecated trait for aggregate shapes" +references = ["smithy-rs#1570"] +meta = { "breaking" = true, "tada" = true, "bug" = false } +author = "weihanglo" diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt index 9505cb4008..057f98d8cb 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerCombinedErrorGeneratorTest.kt @@ -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("error#$it").renderWithModelBuilder(model, symbolProvider, writer, CodegenTarget.SERVER) } val errors = listOf("FooException", "ComplexError", "InvalidGreeting").map { model.lookup("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"); """, ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt index 8cead4f30c..d501849e68 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt @@ -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() { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustWriter.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustWriter.kt index 57ce742b50..36084ff8d3 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustWriter.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustWriter.kt @@ -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.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() ?: 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.escape(text: String): String = text.replace("$expressionStart", "$expressionStart$expressionStart") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt index f3edb33230..4264f26e18 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/AllowLintsGenerator.kt @@ -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 ``. 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 = listOf(), - private val clippyLints: List = ClippyAllowLints, - private val docsLints: List = AllowDocsLints, + private val rustcLints: List = AllowedRustcLints, + private val clippyLints: List = AllowedClippyLints, + private val rustdocLints: List = 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 diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt index 5087727d0f..0ce921f1e4 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGenerator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.asArgument import software.amazon.smithy.rust.codegen.rustlang.asOptional import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.render @@ -124,6 +125,7 @@ class BuilderGenerator( val input = coreType.asArgument("input") writer.documentShape(member, model) + writer.deprecatedShape(member) writer.rustBlock("pub fn $memberName(mut self, ${input.argument}) -> Self") { write("self.$memberName = Some(${input.value});") write("self") @@ -146,6 +148,7 @@ class BuilderGenerator( val inputType = outerType.asOptional() writer.documentShape(member, model) + writer.deprecatedShape(member) writer.rustBlock("pub fn ${member.setterName()}(mut self, input: ${inputType.render(true)}) -> Self") { rust("self.$memberName = input; self") } @@ -195,6 +198,7 @@ class BuilderGenerator( docs("To override the contents of this collection use [`${member.setterName()}`](Self::${member.setterName()}).") rust("///") documentShape(member, model, autoSuppressMissingDocs = false) + deprecatedShape(member) val input = coreType.member.asArgument("input") rustBlock("pub fn $memberName(mut self, ${input.argument}) -> Self") { @@ -215,6 +219,7 @@ class BuilderGenerator( docs("To override the contents of this collection use [`${member.setterName()}`](Self::${member.setterName()}).") rust("///") documentShape(member, model, autoSuppressMissingDocs = false) + deprecatedShape(member) val k = coreType.key.asArgument("k") val v = coreType.member.asArgument("v") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EnumGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EnumGenerator.kt index f9cee41fff..f4c6c55653 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EnumGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/EnumGenerator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.traits.DocumentationTrait import software.amazon.smithy.model.traits.EnumDefinition import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.escape @@ -47,10 +48,17 @@ class EnumMemberModel(private val definition: EnumDefinition, private val symbol ) } + private fun renderDeprecated(writer: RustWriter) { + if (definition.isDeprecated) { + writer.rust("##[deprecated]") + } + } + fun derivedName() = checkNotNull(symbolProvider.toEnumVariantName(definition)).name fun render(writer: RustWriter) { renderDocumentation(writer) + renderDeprecated(writer) writer.write("${derivedName()},") } } @@ -116,6 +124,7 @@ open class EnumGenerator( private fun renderUnnamedEnum() { writer.documentShape(shape, model) + writer.deprecatedShape(shape) meta.render(writer) writer.write("struct $enumName(String);") writer.rustBlock("impl $enumName") { @@ -150,6 +159,7 @@ open class EnumGenerator( shape.getTrait()?.value, renamedWarning.ifBlank { null }, ) + writer.deprecatedShape(shape) meta.render(writer) writer.rustBlock("enum $enumName") { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt index ec6d1f2d7c..fa2b3eb038 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/StructureGenerator.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.asDeref import software.amazon.smithy.rust.codegen.rustlang.asRef +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.isCopy import software.amazon.smithy.rust.codegen.rustlang.isDeref @@ -133,6 +134,7 @@ open class StructureGenerator( // Render field accessor methods forEachMember(accessorMembers) { member, memberName, memberSymbol -> renderMemberDoc(member, memberSymbol) + writer.deprecatedShape(member) val memberType = memberSymbol.rustType() val returnType = when { memberType.isCopy() -> memberType @@ -155,6 +157,7 @@ open class StructureGenerator( open fun renderStructureMember(writer: RustWriter, member: MemberShape, memberName: String, memberSymbol: Symbol) { writer.renderMemberDoc(member, memberSymbol) + writer.deprecatedShape(member) memberSymbol.expectRustMetadata().render(writer) writer.write("$memberName: #T,", symbolProvider.toSymbol(member)) } @@ -163,6 +166,7 @@ open class StructureGenerator( val symbol = symbolProvider.toSymbol(shape) val containerMeta = symbol.expectRustMetadata() writer.documentShape(shape, model) + writer.deprecatedShape(shape) val withoutDebug = containerMeta.derives.copy(derives = containerMeta.derives.derives - RuntimeType.Debug) containerMeta.copy(derives = withoutDebug).render(writer) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/UnionGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/UnionGenerator.kt index 73a425a2ed..50b02fe871 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/UnionGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/UnionGenerator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rust @@ -52,6 +53,7 @@ class UnionGenerator( private fun renderUnion() { writer.documentShape(shape, model) + writer.deprecatedShape(shape) val unionSymbol = symbolProvider.toSymbol(shape) val containerMeta = unionSymbol.expectRustMetadata() @@ -62,6 +64,7 @@ class UnionGenerator( val note = memberSymbol.renamedFrom()?.let { oldName -> "This variant has been renamed from `$oldName`." } documentShape(member, model, note = note) + deprecatedShape(member) memberSymbol.expectRustMetadata().renderAttributes(this) write("${symbolProvider.toMemberName(member)}(#T),", symbolProvider.toSymbol(member)) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientCore.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientCore.kt index 5f8be068d2..383ace2a8a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientCore.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientCore.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.asArgument +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rust @@ -26,6 +27,7 @@ class FluentClientCore(private val model: Model) { val input = coreType.member.asArgument("input") documentShape(member, model) + deprecatedShape(member) rustBlock("pub fn $memberName(mut self, ${input.argument}) -> Self") { write("self.inner = self.inner.$memberName(${input.value});") write("self") @@ -42,6 +44,7 @@ class FluentClientCore(private val model: Model) { val v = coreType.member.asArgument("v") documentShape(member, model) + deprecatedShape(member) rustBlock("pub fn $memberName(mut self, ${k.argument}, ${v.argument}) -> Self") { write("self.inner = self.inner.$memberName(${k.value}, ${v.value});") write("self") @@ -58,6 +61,7 @@ class FluentClientCore(private val model: Model) { val functionInput = coreType.asArgument("input") documentShape(member, model) + deprecatedShape(member) rustBlock("pub fn $memberName(mut self, ${functionInput.argument}) -> Self") { write("self.inner = self.inner.$memberName(${functionInput.value});") write("self") diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt index 3a32e63e09..eaf3c066cd 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/client/FluentClientDecorator.kt @@ -25,6 +25,7 @@ import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.asArgumentType import software.amazon.smithy.rust.codegen.rustlang.asOptional import software.amazon.smithy.rust.codegen.rustlang.asType +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.rustlang.docLink import software.amazon.smithy.rust.codegen.rustlang.docs import software.amazon.smithy.rust.codegen.rustlang.documentShape @@ -466,6 +467,7 @@ class FluentClientGenerator( ) documentShape(operation, model, autoSuppressMissingDocs = false) + deprecatedShape(operation) baseDerives.copy(derives = derives).render(this) rustTemplate( """ diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/CombinedErrorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/CombinedErrorGenerator.kt index d274b0e357..2b7722b585 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/CombinedErrorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/CombinedErrorGenerator.kt @@ -18,6 +18,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.Writable +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock @@ -157,6 +158,7 @@ class CombinedErrorGenerator( writer.rustBlock("enum ${errorSymbol.name}Kind") { errors.forEach { errorVariant -> documentShape(errorVariant, model) + deprecatedShape(errorVariant) val errorVariantSymbol = symbolProvider.toSymbol(errorVariant) write("${errorVariantSymbol.name}(#T),", errorVariantSymbol) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ServerCombinedErrorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ServerCombinedErrorGenerator.kt index 9c3ff7bb76..944a3a358a 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ServerCombinedErrorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/ServerCombinedErrorGenerator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.Writable +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock @@ -53,6 +54,7 @@ open class ServerCombinedErrorGenerator( writer.rustBlock("enum ${errorSymbol.name}") { errors.forEach { errorVariant -> documentShape(errorVariant, model) + deprecatedShape(errorVariant) val errorVariantSymbol = symbolProvider.toSymbol(errorVariant) write("${errorVariantSymbol.name}(#T),", errorVariantSymbol) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGenerator.kt index 2bb8c206fa..b8f7214515 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGenerator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Visibility import software.amazon.smithy.rust.codegen.rustlang.asType +import software.amazon.smithy.rust.codegen.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.rustBlock @@ -131,6 +132,7 @@ class TopLevelErrorGenerator(private val coreCodegenContext: CoreCodegenContext, rustBlock("enum Error") { allErrors.forEach { error -> documentShape(error, model) + deprecatedShape(error) val sym = symbolProvider.toSymbol(error) rust("${sym.name}(#T),", sym) } diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/EnumGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/EnumGeneratorTest.kt index e2ae704a7f..9ec23f11f0 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/EnumGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/EnumGeneratorTest.kt @@ -12,6 +12,7 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.smithy.generators.EnumMemberModel import software.amazon.smithy.rust.codegen.testutil.asSmithyModel @@ -100,13 +101,16 @@ class EnumGeneratorTest { value: "t2.micro", name: "T2_MICRO", documentation: "T2 instances are Burstable Performance Instances.", + deprecated: true, tags: ["ebsOnly"] }, ]) + @deprecated(since: "1.2.3") string InstanceType """.asSmithyModel() val provider = testSymbolProvider(model) val writer = RustWriter.forModule("model") + writer.rust("##![allow(deprecated)]") val shape = model.lookup("test#InstanceType") val generator = EnumGenerator(model, provider, writer, shape, shape.expectTrait()) generator.render() @@ -120,8 +124,12 @@ class EnumGeneratorTest { assert_eq!(InstanceType::from("other").as_str(), "other"); """, ) - - writer.toString() shouldContain "#[non_exhaustive]" + val output = writer.toString() + output shouldContain "#[non_exhaustive]" + // on enum variant `T2Micro` + output shouldContain "#[deprecated]" + // on enum itself + output shouldContain "#[deprecated(since = \"1.2.3\")]" } @Test @@ -165,11 +173,13 @@ class EnumGeneratorTest { { value: "Bar", }]) + @deprecated string FooEnum """.asSmithyModel() val shape = model.lookup("test#FooEnum") val trait = shape.expectTrait() val writer = RustWriter.forModule("model") + writer.rust("##![allow(deprecated)]") val generator = EnumGenerator(model, testSymbolProvider(model), writer, shape, trait) generator.render() writer.compileAndTest( @@ -209,6 +219,7 @@ class EnumGeneratorTest { val trait = shape.expectTrait() val provider = testSymbolProvider(model) val writer = RustWriter.forModule("model") + writer.rust("##![allow(deprecated)]") val generator = EnumGenerator(model, provider, writer, shape, trait) generator.render() writer.compileAndTest( diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt index 9daa063f9c..dd4643333c 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt @@ -35,6 +35,8 @@ class StructureGeneratorTest { foo: String, @documentation("This *is* documentation about the member.") bar: PrimitiveInteger, + // Intentionally deprecated. + @deprecated baz: Integer, ts: Timestamp, inner: Inner, @@ -99,6 +101,7 @@ class StructureGeneratorTest { fun `generate structures with public fields`() { val provider = testSymbolProvider(model) val writer = RustWriter.root() + writer.rust("##![allow(deprecated)]") writer.withModule("model") { val innerGenerator = StructureGenerator(model, provider, this, inner) innerGenerator.render() @@ -209,6 +212,68 @@ class StructureGeneratorTest { ) } + @Test + fun `deprecated trait with message and since`() { + val model = """ + namespace test + + @deprecated + structure Foo {} + + @deprecated(message: "Fly, you fools!") + structure Bar {} + + @deprecated(since: "1.2.3") + structure Baz {} + + @deprecated(message: "Fly, you fools!", since: "1.2.3") + structure Qux {} + """.asSmithyModel() + val provider = testSymbolProvider(model) + val writer = RustWriter.root() + writer.rust("##![allow(deprecated)]") + writer.withModule("model") { + StructureGenerator(model, provider, this, model.lookup("test#Foo")).render() + StructureGenerator(model, provider, this, model.lookup("test#Bar")).render() + StructureGenerator(model, provider, this, model.lookup("test#Baz")).render() + StructureGenerator(model, provider, this, model.lookup("test#Qux")).render() + } + + // turn on clippy to check the semver-compliant version of `since`. + writer.compileAndTest(clippy = true) + } + + @Test + fun `nested deprecated trait`() { + val model = """ + namespace test + + structure Nested { + foo: Foo, + @deprecated + foo2: Foo, + } + + @deprecated + structure Foo { + bar: Bar, + } + + @deprecated + structure Bar {} + """.asSmithyModel() + val provider = testSymbolProvider(model) + val writer = RustWriter.root() + writer.rust("##![allow(deprecated)]") + writer.withModule("model") { + StructureGenerator(model, provider, this, model.lookup("test#Nested")).render() + StructureGenerator(model, provider, this, model.lookup("test#Foo")).render() + StructureGenerator(model, provider, this, model.lookup("test#Bar")).render() + } + + writer.compileAndTest() + } + @Test fun `it generates accessor methods`() { val testModel = diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/UnionGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/UnionGeneratorTest.kt index 8663f3b2f0..ce3708a000 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/UnionGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/UnionGeneratorTest.kt @@ -9,6 +9,7 @@ import io.kotest.matchers.string.shouldContain import org.junit.jupiter.api.Test import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.testutil.compileAndTest @@ -95,6 +96,34 @@ class UnionGeneratorTest { ) } + @Test + fun `generate deprecated unions`() { + val model = """namespace test + union Nested { + foo: Foo, + @deprecated + foo2: Foo, + } + @deprecated + union Foo { + bar: Bar, + } + + @deprecated + union Bar {} + """.asSmithyModel() + val provider: SymbolProvider = testSymbolProvider(model) + val writer = RustWriter.root() + writer.rust("##![allow(deprecated)]") + writer.withModule("model") { + UnionGenerator(model, provider, this, model.lookup("test#Nested")).render() + UnionGenerator(model, provider, this, model.lookup("test#Foo")).render() + UnionGenerator(model, provider, this, model.lookup("test#Bar")).render() + } + + writer.compileAndTest() + } + private fun generateUnion(modelSmithy: String, unionName: String = "MyUnion", unknownVariant: Boolean = true): RustWriter { val model = "namespace test\n$modelSmithy".asSmithyModel() val provider: SymbolProvider = testSymbolProvider(model) diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGeneratorTest.kt index 85dcfae326..bd3eceeaf6 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/BuilderGeneratorTest.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.traits.EnumDefinition import software.amazon.smithy.rust.codegen.generators.StructureGeneratorTest import software.amazon.smithy.rust.codegen.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.smithy.Default import software.amazon.smithy.rust.codegen.smithy.MaybeRenamed import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider @@ -29,6 +30,7 @@ internal class BuilderGeneratorTest { fun `generate builders`() { val provider = testSymbolProvider(model) val writer = RustWriter.forModule("model") + writer.rust("##![allow(deprecated)]") val innerGenerator = StructureGenerator(model, provider, writer, inner) val generator = StructureGenerator(model, provider, writer, struct) val builderGenerator = BuilderGenerator(model, provider, struct) @@ -69,6 +71,7 @@ internal class BuilderGeneratorTest { } } val writer = RustWriter.forModule("model") + writer.rust("##![allow(deprecated)]") val innerGenerator = StructureGenerator( StructureGeneratorTest.model, provider, writer, StructureGeneratorTest.inner, diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CombinedErrorGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CombinedErrorGeneratorTest.kt index e57a0ef355..ac09b81bac 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CombinedErrorGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CombinedErrorGeneratorTest.kt @@ -23,7 +23,7 @@ class CombinedErrorGeneratorTest { namespace error operation Greeting { - errors: [InvalidGreeting, ComplexError, FooException] + errors: [InvalidGreeting, ComplexError, FooException, Deprecated] } @error("client") @@ -40,6 +40,10 @@ class CombinedErrorGeneratorTest { abc: String, other: Integer } + + @error("server") + @deprecated + structure Deprecated { } """.asSmithyModel() private val model = OperationNormalizer.transform(baseModel) private val symbolProvider = testSymbolProvider(model) @@ -48,7 +52,7 @@ class CombinedErrorGeneratorTest { 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("error#$it").renderWithModelBuilder(model, symbolProvider, writer) } val errors = listOf("FooException", "ComplexError", "InvalidGreeting").map { model.lookup("error#$it") } @@ -80,7 +84,10 @@ class CombinedErrorGeneratorTest { // Indicate the original name in the display output. let error = FooError::builder().build(); - assert_eq!(format!("{}", error), "FooError [FooException]") + assert_eq!(format!("{}", error), "FooError [FooException]"); + + let error = Deprecated::builder().build(); + assert_eq!(error.to_string(), "Deprecated"); """, ) diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGeneratorTest.kt index 70f4721ba1..69fa42e0c2 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/error/TopLevelErrorGeneratorTest.kt @@ -31,7 +31,7 @@ internal class TopLevelErrorGeneratorTest { @http(uri: "/", method: "POST") operation SayHello { - errors: [SorryBusy, CanYouRepeatThat] + errors: [SorryBusy, CanYouRepeatThat, MeDeprecated] } @@ -40,6 +40,10 @@ internal class TopLevelErrorGeneratorTest { @error("client") structure CanYouRepeatThat { } + + @error("client") + @deprecated + structure MeDeprecated { } """.asSmithyModel() val (pluginContext, testDir) = generatePluginContext(model) diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/lang/RustWriterTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/lang/RustWriterTest.kt index 8b442f6c41..13a63efbd0 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/lang/RustWriterTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/lang/RustWriterTest.kt @@ -132,6 +132,34 @@ class RustWriterTest { sut.toString().shouldContain("#[foo]\n/// heres an attribute") } + @Test + fun `deprecated attribute without any field`() { + val sut = RustWriter.forModule("lib") + Attribute.Custom.deprecated().render(sut) + sut.toString() shouldContain "#[deprecated]" + } + + @Test + fun `deprecated attribute with a note`() { + val sut = RustWriter.forModule("lib") + Attribute.Custom.deprecated("custom").render(sut) + sut.toString() shouldContain "#[deprecated(note = \"custom\")]" + } + + @Test + fun `deprecated attribute with a since`() { + val sut = RustWriter.forModule("lib") + Attribute.Custom.deprecated(since = "1.2.3").render(sut) + sut.toString() shouldContain "#[deprecated(since = \"1.2.3\")]" + } + + @Test + fun `deprecated attribute with a note and a since`() { + val sut = RustWriter.forModule("lib") + Attribute.Custom.deprecated("custom", "1.2.3").render(sut) + sut.toString() shouldContain "#[deprecated(note = \"custom\", since = \"1.2.3\")]" + } + @Test fun `template writables with upper case names`() { val inner = writable { rust("hello") }