Skip to content

Commit 1e28b74

Browse files
committed
Allow empty lines without indenation in multi-line string literals
1 parent 9c2cdea commit 1e28b74

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

Sources/SwiftParser/StringLiterals.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ extension Parser {
210210
segment.unexpectedAfterContent,
211211
arena: self.arena
212212
)
213+
} else if segment.content.tokenText == "" || segment.content.tokenText.triviaPieceIfNewline != nil {
214+
// Empty lines don't need to be indented and there's no indentation we need to strip away.
213215
} else {
214216
let actualIndentation = segment.content.tokenText.prefix(while: { $0 == UInt8(ascii: " ") || $0 == UInt8(ascii: "\t") })
215217
let actualIndentationTrivia = TriviaParser.parseTrivia(SyntaxText(rebasing: actualIndentation), position: .leading)

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,76 @@ final class ExpressionTests: XCTestCase {
10251025
"""#
10261026
)
10271027
}
1028+
1029+
func testEmptyLineInMultilineStringLiteral() {
1030+
AssertParse(
1031+
#"""
1032+
"""
1033+
line 1
1034+
1035+
line 2
1036+
"""
1037+
"""#,
1038+
substructure: Syntax(
1039+
StringLiteralExprSyntax(
1040+
openDelimiter: nil,
1041+
openQuote: .multilineStringQuoteToken(leadingTrivia: [.spaces(2)], trailingTrivia: .newline),
1042+
segments: StringLiteralSegmentsSyntax([
1043+
.stringSegment(StringSegmentSyntax(content: .stringSegment("line 1\n", leadingTrivia: [.spaces(2)]))),
1044+
.stringSegment(StringSegmentSyntax(content: .stringSegment("\n"))),
1045+
.stringSegment(StringSegmentSyntax(content: .stringSegment("line 2", leadingTrivia: [.spaces(2)], trailingTrivia: .newline))),
1046+
]),
1047+
closeQuote: .multilineStringQuoteToken(leadingTrivia: [.spaces(2)]),
1048+
closeDelimiter: nil
1049+
)
1050+
),
1051+
options: [.substructureCheckTrivia]
1052+
)
1053+
1054+
AssertParse(
1055+
#"""
1056+
"""
1057+
line 1
1058+
1059+
"""
1060+
"""#,
1061+
substructure: Syntax(
1062+
StringLiteralExprSyntax(
1063+
openDelimiter: nil,
1064+
openQuote: .multilineStringQuoteToken(leadingTrivia: [.spaces(2)], trailingTrivia: .newline),
1065+
segments: StringLiteralSegmentsSyntax([
1066+
.stringSegment(StringSegmentSyntax(content: .stringSegment("line 1\n", leadingTrivia: [.spaces(2)]))),
1067+
.stringSegment(StringSegmentSyntax(content: .stringSegment("", trailingTrivia: .newline))),
1068+
]),
1069+
closeQuote: .multilineStringQuoteToken(leadingTrivia: [.spaces(2)]),
1070+
closeDelimiter: nil
1071+
)
1072+
),
1073+
options: [.substructureCheckTrivia]
1074+
)
1075+
}
1076+
1077+
func testUnderIndentedWhitespaceonlyLineInMultilineStringLiteral() {
1078+
AssertParse(
1079+
#"""
1080+
"""
1081+
line 1
1082+
1️⃣
1083+
line 2
1084+
"""
1085+
"""#,
1086+
diagnostics: [
1087+
DiagnosticSpec(message: "insufficient indentation of line in multi-line string literal")
1088+
],
1089+
fixedSource: #"""
1090+
"""
1091+
line 1
1092+
\#(" ")
1093+
line 2
1094+
"""
1095+
"""#
1096+
)
1097+
}
10281098
}
10291099

10301100
final class MemberExprTests: XCTestCase {

0 commit comments

Comments
 (0)