Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix escaping of Self in symbol providers #2381

Merged
merged 2 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
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)
}
Original file line number Diff line number Diff line change
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