Unverified Commit 08c16d31 authored by John DiSanti's avatar John DiSanti Committed by GitHub
Browse files

Fix escaping of `Self` in symbol providers (#2381)

* Fix escaping of `Self` in symbol providers

* Clean up an old hack
parent 6dd7bc76
Loading
Loading
Loading
Loading
+19 −17
Original line number Diff line number Diff line
@@ -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)
@@ -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
@@ -103,7 +96,7 @@ class RustReservedWordSymbolProvider(private val base: RustSymbolProvider) : Wra
                    }.build()
            }

            else -> base.toSymbol(shape)
            else -> renamedSymbol
        }
    }
}
@@ -165,11 +158,20 @@ 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)) {
@@ -177,5 +179,5 @@ object RustReservedWords : ReservedWords {
        else -> word
    }

    override fun isReserved(word: String): Boolean = RustKeywords.contains(word)
    override fun isReserved(word: String): Boolean = RustKeywords.contains(word) || keywordEscapingMap.contains(word)
}
+13 −1
Original line number Diff line number Diff line
@@ -16,11 +16,23 @@ 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 = """
@@ -28,7 +40,7 @@ internal class RustReservedWordSymbolProviderTest {
            structure container {
                async: String
            }
        """.trimMargin().asSmithyModel()
        """.asSmithyModel()
        val provider = RustReservedWordSymbolProvider(TestSymbolProvider(model))
        provider.toMemberName(
            MemberShape.builder().id("namespace#container\$async").target("namespace#Integer").build(),