uniffi-rs icon indicating copy to clipboard operation
uniffi-rs copied to clipboard

swift: surround identifiers with backticks to avoid reserved word conflicts

Open eugene-babichenko opened this issue 3 years ago • 10 comments

Sometimes identifiers from a UDL file may conflict with Swift reserved words. Swift allows to resolve such conflicts by surrounding an identifier with backticks (`). For example:

var private: Bool

is not a valid expression, but

var `private`: Bool

is.

This pull request adds backticks automatically when required. Sometimes they may be redundant, but this does not trigger a compilation error and is a purely stylistic issue, so we rely on swiftformat to remove them when they are unnecessary.

The list of keywords was taken from Swift documentation. I skipped keywords that I think are not valid UDL identifiers.

eugene-babichenko avatar Feb 11 '22 22:02 eugene-babichenko

This sounds like a reasonable idea, thanks! Is there a good reason to not just quote every name instead of just reserved words?

mhammond avatar Feb 14 '22 02:02 mhammond

Is there a good reason to not just quote every name instead of just reserved words?

Honestly, there's none. And doing it that way the formatter would remove them anyway if they are not needed. I can make a change to quote everything and rely on the formatter.

P.S. if you think of it a bit, such solution is more future-proof :)

eugene-babichenko avatar Feb 21 '22 10:02 eugene-babichenko

I don't love this patch: I think I'd rather error out instead of allow using reserved words.

However, if you were unfortunate enough to design an API for one language, (e.g. you use val in Swift), then decide to adopt Kotlin, where val is a reserved word, then that would mean some unscheduled API evolution for your Swift clients.

In that case, there'd be multiple paths:

  • detect the error, and error out. sorry, :( you have to eat the cost of evolving the API in Swift.
  • backtick the val in Kotlin, as in this patch
  • backtick everything, and hope the formatter (if available) cleans up the identifiers that we didn't need backticks for.
  • for languages that don't support backticks, we error out anyway?

Is there a good reason to not just quote every name instead of just reserved words?

If we are going to backtick, I think I'd rather not rely on the formatter to clean up the code; why not produce nice code to start with?

In that case, in order to make this PR landable it should have corresponding checks and reserved word lists for Kotlin and Python (do we still support Ruby?).

jhugman avatar Mar 06 '22 18:03 jhugman

if you were unfortunate enough to design an API for one language, (e.g. you use val in Swift), then decide to adopt Kotlin

I'm not quite sure I agree with that TBH. In a world where binding generators can be in different crates (eg, #1150), I could imagine a world where "personA" develops a nice rust package with Uniffi support, "personB" develops a separate binding generator for some obscure language, then "person C" comes alone and wants to use PersonA's library with the language binding by PersonB - there's nothing reasonable we can ask PersonC to do if PersonA happened to use an identifier that is a reserved word in PersonB's binding.

If we are going to backtick, I think I'd rather not rely on the formatter to clean up the code; why not produce nice code to start with?

I think that's a reflection on my suggestion that instead of hard-coding all reserved words for a language (which may be fragile across language versions), I suggested backticking everything, then the OP noted that the formatter would clean this up. IOW, have the language formatter be the only thing that needs to know the exact set of reserved words.

In that case, in order to make this PR landable it should have corresponding checks and reserved word lists for Kotlin

It already does though, right? [edit: oops, this PR has swift, not kotlin! I'm not sure why it's a requirement to "fix" all languages or fix none of them though? Can't we make this a non-problem for swift and leave the making it a non-problem for other bindings for others in the future?]

mhammond avatar Mar 08 '22 00:03 mhammond

I think different bindings are going to want different solutions. For Swift, Kotlin, and other languages that support some quoting method, we should try to do that. For Python, Ruby and other languages that don't support quoting, we could transform the name (for -> arg_for or something like that). That's not great, but I think it's better than failing. Given that we need to put some thought into how each binding wants to handle this issue, I'm happy to accept a change that only targets Kotlin, then work on the rest later.

What's the worst downside to producing uglier bindings code? This is a question I run into a lot. My instinct is to make everything look nice, but then when I stop to think about it I don't have a strong justification. Pretty code is good when you're trying to read the generated code to debug a bindings issue. But for that use case we can make sure the formatter is run beforehand.

bendk avatar Mar 09 '22 15:03 bendk

Thanks Ben - that's my take on the formatter too - instinct is to make it pretty, but I can't justify that when I think more about it, especially now that good formatters exist - and these formatters tend to enforce a very explicit code-style, so trying to make sure we generate pretty code that conforms to some code style is really a bridge too far!

@eugene-babichenko do you intend updating this PR?

mhammond avatar Mar 09 '22 21:03 mhammond

do you intend updating this PR?

So after all the consensus is to generate uglier code, right?

eugene-babichenko avatar Mar 10 '22 20:03 eugene-babichenko

So after all the consensus is to generate uglier code, right?

Yep, thanks for your patience!

mhammond avatar Mar 16 '22 00:03 mhammond

We ran into problems with the reserved Swift keyword internal for bdk-swift. I think this would have prevented the issue, and I just wanted to give a thumbs up for the feature to be added in the future!

thunderbiscuit avatar Sep 23 '22 16:09 thunderbiscuit

We ran into problems with the reserved Swift keyword internal for bdk-swift. I think this would have prevented the issue, and I just wanted to give a thumbs up for the feature to be added in the future!

For a little more context in our bdk-ffi project we're using uniffi-rs 0.19.3 which should already include #1237. But it looks like that PR doesn't work for Swift, and I'm not sure if it handles enum variants with a reserved word name, we had an variant named "internal" which is not allowed in Swift (but is OK in rust, python, kotlin).

notmandatory avatar Sep 23 '22 16:09 notmandatory

I think this one can be closed now that #1350 is merged. Thanks @eugene-babichenko for your research here, it was a big help.

notmandatory avatar Oct 02 '22 21:10 notmandatory