Skip to content

Commit

Permalink
Fix escaping of Self in symbol providers (smithy-lang#2381)
Browse files Browse the repository at this point in the history
* Fix escaping of `Self` in symbol providers

* Clean up an old hack
  • Loading branch information
jdisanti committed Feb 22, 2023
1 parent 6dd7bc7 commit 08c16d3
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 18 deletions.
Expand Up @@ -23,7 +23,10 @@ import software.amazon.smithy.rust.codegen.core.util.letIf

class RustReservedWordSymbolProvider(private val base: RustSymbolProvider) : WrappingSymbolProvider(base) {
private val internal =
ReservedWordSymbolProvider.builder().symbolProvider(base).memberReservedWords(RustReservedWords).build()
ReservedWordSymbolProvider.builder().symbolProvider(base)
.nameReservedWords(RustReservedWords)
.memberReservedWords(RustReservedWords)
.build()

override fun toMemberName(shape: MemberShape): String {
val baseName = super.toMemberName(shape)
Expand All @@ -49,20 +52,10 @@ class RustReservedWordSymbolProvider(private val base: RustSymbolProvider) : Wra
// that represent union variants that have been added since this SDK was generated.
UnionGenerator.UnknownVariantName -> "${UnionGenerator.UnknownVariantName}Value"
"${UnionGenerator.UnknownVariantName}Value" -> "${UnionGenerator.UnknownVariantName}Value_"
// Self cannot be used as a raw identifier, so we can't use the normal escaping strategy
// https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/4
"Self" -> "SelfValue"
// Real models won't end in `_` so it's safe to stop here
"SelfValue" -> "SelfValue_"
else -> reservedWordReplacedName
}

container is EnumShape || container.hasTrait<EnumTrait>() -> when (baseName) {
// Self cannot be used as a raw identifier, so we can't use the normal escaping strategy
// https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/4
"Self" -> "SelfValue"
// Real models won't end in `_` so it's safe to stop here
"SelfValue" -> "SelfValue_"
// Unknown is used as the name of the variant containing unexpected values
"Unknown" -> "UnknownValue"
// Real models won't end in `_` so it's safe to stop here
Expand Down Expand Up @@ -103,7 +96,7 @@ class RustReservedWordSymbolProvider(private val base: RustSymbolProvider) : Wra
}.build()
}

else -> base.toSymbol(shape)
else -> renamedSymbol
}
}
}
Expand Down Expand Up @@ -165,17 +158,26 @@ object RustReservedWords : ReservedWords {
"try",
)

private val cantBeRaw = setOf("self", "crate", "super")
// Some things can't be used as a raw identifier, so we can't use the normal escaping strategy
// https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/4
private val keywordEscapingMap = mapOf(
"crate" to "crate_",
"super" to "super_",
"self" to "self_",
"Self" to "SelfValue",
// Real models won't end in `_` so it's safe to stop here
"SelfValue" to "SelfValue_",
)

override fun escape(word: String): String = when {
cantBeRaw.contains(word) -> "${word}_"
else -> "r##$word"
override fun escape(word: String): String = when (val mapped = keywordEscapingMap[word]) {
null -> "r##$word"
else -> mapped
}

fun escapeIfNeeded(word: String): String = when (isReserved(word)) {
true -> escape(word)
else -> word
}

override fun isReserved(word: String): Boolean = RustKeywords.contains(word)
override fun isReserved(word: String): Boolean = RustKeywords.contains(word) || keywordEscapingMap.contains(word)
}
Expand Up @@ -16,19 +16,31 @@ import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider
import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom
import software.amazon.smithy.rust.codegen.core.testutil.TestRustSymbolProviderConfig
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rust.codegen.core.util.lookup

internal class RustReservedWordSymbolProviderTest {
private class TestSymbolProvider(model: Model) :
WrappingSymbolProvider(SymbolVisitor(model, null, TestRustSymbolProviderConfig))

@Test
fun `structs are escaped`() {
val model = """
namespace test
structure Self {}
""".asSmithyModel()
val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model))
val symbol = provider.toSymbol(model.lookup("test#Self"))
symbol.name shouldBe "SelfValue"
}

@Test
fun `member names are escaped`() {
val model = """
namespace namespace
structure container {
async: String
}
""".trimMargin().asSmithyModel()
""".asSmithyModel()
val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model))
provider.toMemberName(
MemberShape.builder().id("namespace#container\$async").target("namespace#Integer").build(),
Expand Down

0 comments on commit 08c16d3

Please sign in to comment.