Skip to content

Commit

Permalink
Basic support of @deprecated trait in Smithy model (#1570)
Browse files Browse the repository at this point in the history
* 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: Weihang Lo <[email protected]>

* Test `@deprecated` trait for RustWriter

* Support `@deprecated` trait for StructureGenerator

* Support `@deprecated` trait for UnionGenerator

Signed-off-by: Weihang Lo <[email protected]>

* 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: Weihang Lo <[email protected]>

* Leverage Kotlin null safety well

Signed-off-by: Weihang Lo <[email protected]>

* Allow `deprecated` rustc lint rule

Signed-off-by: Weihang Lo <[email protected]>

* Allow deprecated in unit tests

Signed-off-by: Weihang Lo <[email protected]>

* Leverage kotlin null safety check again

Signed-off-by: Weihang Lo <[email protected]>

* changelog: Support @deprecated trait

Signed-off-by: Weihang Lo <[email protected]>

Co-authored-by: Matteo Bigoi <[email protected]>
  • Loading branch information
weihanglo and crisidev authored Jul 28, 2022
1 parent 74a106f commit e38db43
Show file tree
Hide file tree
Showing 21 changed files with 255 additions and 17 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.next.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
author = "lkts"

[[smithy-rs]]
message = "Support @deprecated trait for aggregate shapes"
references = ["smithy-rs#1570"]
meta = { "breaking" = true, "tada" = true, "bug" = false }
author = "weihanglo"
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ServerCombinedErrorGeneratorTest {
namespace error
operation Greeting {
errors: [InvalidGreeting, ComplexError, FooException]
errors: [InvalidGreeting, ComplexError, FooException, Deprecated]
}
@error("client")
Expand All @@ -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)
Expand All @@ -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") }
Expand All @@ -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");
""",
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand All @@ -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")
}
Expand Down Expand Up @@ -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") {
Expand All @@ -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")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()},")
}
}
Expand Down Expand Up @@ -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") {
Expand Down Expand Up @@ -150,6 +159,7 @@ open class EnumGenerator(
shape.getTrait<DocumentationTrait>()?.value,
renamedWarning.ifBlank { null },
)
writer.deprecatedShape(shape)

meta.render(writer)
writer.rustBlock("enum $enumName") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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))
}
Expand All @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand All @@ -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")
Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -466,6 +467,7 @@ class FluentClientGenerator(
)

documentShape(operation, model, autoSuppressMissingDocs = false)
deprecatedShape(operation)
baseDerives.copy(derives = derives).render(this)
rustTemplate(
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
Loading

0 comments on commit e38db43

Please sign in to comment.