I've played around with the sample repo and built a simple macro to create compile time checked URLs.
Motivation
This is similar in spirit to compile time checked regular expressions. For a literal Regex
the compiler makes sure that the regex has no syntactic errors by parsing it at compile time. In case of a syntax error the compiler emits an error (and does not produce any code).
When the regex parses without error a non-optional, non-throwing initializer is inserted. During runtime the Regex has to be parsed again to create the actual instance but it is known to not throw an error.
This behavior, where the compiler checks the validity of an initializer's arguments would be helpful for many other types, for example a SemVer type that checks for a valid semantic version.
In these cases macros seem like a good workaround (until we get statically checked arguments).
So the macro would take the provided string literal and, during compile time, check if it's valid.
let url = #URL("https://swift.org/") // fine: url is a non-optional URL
let bad = #URL("not a url") // emits error
This went very well and I'm happy with the result. Declaring and implementing the macro was straight forward and the emitted diagnostics are as expected. There's even unit test support which is nice and helpful in an environment with limited debug tooling.
With one detail I could need some help, though:
The macro implementation needs to access the string value of the passed literal. It is of type StringSegmentSyntax
which can be converted to a String
using segment.content.text
. The result is the verbatim source text between the double quotes from the original string literal. In most cases this is fine.
But when there are escaped characters in the literal, like in "https://swift\u{2e}org/"
, the macro fails. (\u{2e}
is the escaped ASCII '.'
character.) With my limited experience in SwiftSyntax I have not managed to get an unescaped string from the StringSegmentSyntax
.