Skip to content

Commit f4cc24d

Browse files
committed
Fix bugs in builder raw literal analysis
There were two bugs in the way StringLiteralExpr.init(… content: …) determined the necessary raw literal separator: 1. It assumed that only interpolations in a raw literal would require # signs, when in fact all escape sequences do. 2. It did not handle certain edge cases correctly. Rewrite the `StringLiteralExpr.requiresEscaping(_:)` helper method to implement the correct logic, and simplify the implementation as a result.
1 parent 9b96da4 commit f4cc24d

File tree

3 files changed

+29
-20
lines changed

3 files changed

+29
-20
lines changed

Sources/SwiftSyntaxBuilder/ConvenienceInitializers.swift

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -241,34 +241,28 @@ extension StringLiteralExpr {
241241
}
242242

243243
private static func requiresEscaping(_ content: String) -> (Bool, poundCount: Int) {
244-
var state: PoundState = .none
244+
var countingPounds = false
245245
var consecutivePounds = 0
246246
var maxPounds = 0
247247
var requiresEscaping = false
248248

249249
for c in content {
250-
switch c {
251-
case "#":
250+
switch (countingPounds, c) {
251+
// Normal mode: scanning for characters that can be followed by pounds.
252+
case (false, "\""), (false, "\\"):
253+
countingPounds = true
254+
requiresEscaping = true
255+
case (false, _):
256+
continue
257+
258+
// Special mode: counting a sequence of pounds until we reach its end.
259+
case (true, "#"):
252260
consecutivePounds += 1
253-
case "\"":
254-
state = .afterQuote
255-
consecutivePounds = 0
256-
case "\\":
257-
state = .afterBackslash
258-
consecutivePounds = 0
259-
case "(" where state == .afterBackslash:
260261
maxPounds = max(maxPounds, consecutivePounds)
261-
fallthrough
262-
default:
262+
case (true, _):
263+
countingPounds = false
263264
consecutivePounds = 0
264-
state = .none
265-
}
266-
267-
if state == .afterQuote {
268-
maxPounds = max(maxPounds, consecutivePounds)
269265
}
270-
271-
requiresEscaping = requiresEscaping || state != .none
272266
}
273267

274268
return (requiresEscaping, poundCount: maxPounds)

Tests/SwiftSyntaxBuilderTest/StringLiteralTests.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,21 @@ final class StringLiteralTests: XCTestCase {
8181
##"""
8282
#"\"#
8383
"""##)
84+
85+
AssertBuildResult(StringLiteralExpr(content: ##"\#n"##),
86+
##"""
87+
##"\#n"##
88+
"""##)
89+
90+
AssertBuildResult(StringLiteralExpr(content: ##"\#\"##),
91+
##"""
92+
##"\#\"##
93+
"""##)
94+
95+
AssertBuildResult(StringLiteralExpr(content: ##"\#"##),
96+
##"""
97+
##"\#"##
98+
"""##)
8499
}
85100

86101
func testNewlines() {

Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ final class MacroSystemTests: XCTestCase {
6666
_ = ({ () -> Bool in
6767
print("hello")
6868
return true
69-
}, #"{ () -> Bool in\\n print("hello")\\n return true\\n}"#)
69+
}, #"{ () -> Bool in\\#n print("hello")\\#n return true\\#n}"#)
7070
"""
7171
)
7272
}

0 commit comments

Comments
 (0)