Ensure FormatRawStringLiteral produces valid swift syntax for special characters#3256
Conversation
|
Hey @itsdevandy! 👋 I'm just a guest here looking to contribute, and I actually started working on this same issue today before realizing you had already been working it! I've closed my PR to defer to yours. While I was exploring the issue, I did notice one small edge case: checking I wrote a little snippet to handle the tricky // Logic to safely check implementation
var shouldRemoveHashes = maximumHashes == 0
let quote = lit.openingQuote.text
if shouldRemoveHashes {
if quote == "\"" {
// Check for the #"""..."""# case which parses as quote=""" and content wrapped in quotes.
for segment in lit.segments {
if case .stringSegment(let s) = segment {
if s.content.text.hasPrefix("\"") && s.content.text.hasSuffix("\"") {
shouldRemoveHashes = false
break
}
}
}
} else if quote == "\"\"\"" {
// Check for single-line multiline strings
let containsNewline =
lit.openingQuote.trailingTrivia.description.contains("\n")
|| lit.segments.contains(where: { $0.description.contains("\n") })
|| lit.closingQuote.leadingTrivia.description.contains("\n")
if !containsNewline {
shouldRemoveHashes = false
}
}
}Thanks. Rick. |
|
Thanks @rickhohler, appreciate it! Sorry for the delayed response. I’ve started with a basic implementation for now, but I’m wondering how far we should take the refactoring. If we want to be comprehensive, we’d need to account for cases like:
I’m happy to expand the scope to include these and use your suggested logic, but I wanted to check in first. @ahoppen , do you have a preference on the scope here? Thanks. |
All of these cases should be handled. One simple thing you could do to check if we can remove all pounds without re-implementing logic from the lexer, might to parse each string literal segment again without the surrounding |
Thanks, that makes sense. I'm working on implementing it this way. |
Summary
Currently, the
FormatRawStringLiteralrefactoring can produce invalid Swift code when moving from extended delimiters to a regular string literal. For example:#"""#becomes"""(Unmatched multi-line literal)#"He says "Hi""#becomes"He says "Hi""(Syntax error)#"C:\Users"#becomes"C:\Users"(Invalid escape sequence)This PR adjusts the logic to ensure that the minimum number of # symbols is is always sufficient to represent the content safely. The minimum safe number of # delimiters is now 1.
Alternative: Auto-escaping on hitting string literal
I have also explored an implementation where it can convert these to string literals while injecting the escape characters ( eg:
#"He says "Hi""#->"He says \"Hi\"").I can use this alternative, or a different approach if the preference is to favor standard literals over delimited ones. Open to suggestions!
Test Plan:
swift format -iprLinked Issue
Closes swiftlang/sourcekit-lsp#2465