Skip to content

Commit 28c3862

Browse files
committed
Add "Is Multi-Line String" Bit to Lexeme Flags
Use this to prevent the parser from having to re-infer the number of quotes it needs to eat to make progress.
1 parent 6164a7e commit 28c3862

File tree

3 files changed

+29
-3
lines changed

3 files changed

+29
-3
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,8 @@ extension Parser {
10641064
/// Parse open quote.
10651065
let openQuote = self.parseStringLiteralQuote(
10661066
at: openDelimiter != nil ? .leadingRaw : .leading,
1067-
text: text
1067+
text: text,
1068+
wantsMultiline: self.currentToken.isMultilineStringLiteral
10681069
) ?? RawTokenSyntax(missing: .stringQuote, arena: arena)
10691070
if !openQuote.isMissing {
10701071
text = text.dropFirst(openQuote.tokenText.count)
@@ -1078,7 +1079,8 @@ extension Parser {
10781079
/// Parse close quote.
10791080
let closeQuote = self.parseStringLiteralQuote(
10801081
at: openDelimiter != nil ? .trailingRaw : .trailing,
1081-
text: text
1082+
text: text,
1083+
wantsMultiline: self.currentToken.isMultilineStringLiteral
10821084
) ?? RawTokenSyntax(missing: openQuote.tokenKind, arena: arena)
10831085
if !closeQuote.isMissing {
10841086
text = text.dropFirst(closeQuote.tokenText.count)
@@ -1209,7 +1211,8 @@ extension Parser {
12091211

12101212
mutating func parseStringLiteralQuote(
12111213
at position: QuotePosition,
1212-
text: Slice<SyntaxText>
1214+
text: Slice<SyntaxText>,
1215+
wantsMultiline: Bool
12131216
) -> RawTokenSyntax? {
12141217
// Single quote. We only support single line literal.
12151218
if let first = text.first, first == UInt8(ascii: "'") {
@@ -1223,6 +1226,9 @@ extension Parser {
12231226
while index < text.endIndex && text[index] == UInt8(ascii: "\"") {
12241227
quoteCount += 1
12251228
index = text.index(after: index)
1229+
guard wantsMultiline else {
1230+
break
1231+
}
12261232
}
12271233

12281234
// Empty single line string. Return only the first quote.

Sources/SwiftParser/Lexer.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public struct Lexer {
3131
}
3232

3333
public static let isAtStartOfLine = Flags(rawValue: 1 << 0)
34+
public static let isMultilineStringLiteral = Flags(rawValue: 1 << 1)
3435
}
3536

3637
@_spi(RawSyntax)

Tests/SwiftParserTest/Expressions.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,4 +522,23 @@ final class ExpressionTests: XCTestCase {
522522
var pair : (Int, Double) = makePair(a: 1, b: 2.5)
523523
""")
524524
}
525+
526+
// N.B. This test includes zero-width characters that may not render in most
527+
// text editors. Be very careful editing these strings.
528+
//
529+
// See https://github.com/apple/swift/issues/51192 for more context here.
530+
func testFalseMultilineDelimiters() {
531+
AssertParse(
532+
###"""
533+
_ = #"​"​"#
534+
535+
_ = #""""#
536+
537+
_ = #"""""#
538+
539+
_ = #""""""#
540+
541+
_ = ##""" foo # "# "##
542+
"""###)
543+
}
525544
}

0 commit comments

Comments
 (0)