Skip to content

Commit fea17f2

Browse files
johnno1962milseman
authored andcommitted
[Parse] Add support for multiline strings inside interpolations
Adds support for multiline string literals to appear inside of string interpolations. Tests added.
1 parent 5254db4 commit fea17f2

File tree

5 files changed

+43
-23
lines changed

5 files changed

+43
-23
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,6 @@ ERROR(lex_illegal_multiline_string_end,none,
134134
"invalid end of multi-line string literal", ())
135135
ERROR(lex_ambiguous_string_indent,none,
136136
"invalid mix of multi-line string literal indentation", ())
137-
ERROR(lex_multiline_inside_interpolation,none,
138-
"multi-line string literals are not allowed inside an interpolation", ())
139137

140138

141139
ERROR(lex_invalid_character,none,

include/swift/Parse/Lexer.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,9 +519,6 @@ class Lexer {
519519
/// Try to lex conflict markers by checking for the presence of the start and
520520
/// end of the marker in diff3 or Perforce style respectively.
521521
bool tryLexConflictMarker();
522-
523-
/// Check multiline string literal is indented correctly.
524-
void validateMultilineIndents(const Token &Str);
525522
};
526523

527524
} // end namespace swift

lib/Parse/Lexer.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,7 +1240,8 @@ unsigned Lexer::lexCharacter(const char *&CurPtr, char StopQuote,
12401240
/// outstanding delimiters as it scans the string.
12411241
static const char *skipToEndOfInterpolatedExpression(const char *CurPtr,
12421242
const char *EndPtr,
1243-
DiagnosticEngine *Diags) {
1243+
DiagnosticEngine *Diags,
1244+
bool MultilineString) {
12441245
llvm::SmallVector<char, 4> OpenDelimiters;
12451246
auto inStringLiteral = [&]() {
12461247
return !OpenDelimiters.empty() &&
@@ -1257,9 +1258,11 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr,
12571258
// issues with malformed tokens or other problems.
12581259
switch (*CurPtr++) {
12591260
// String literals in general cannot be split across multiple lines;
1260-
// interpolated ones are no exception.
1261+
// interpolated ones are no exception - unless multiline literals.
12611262
case '\n':
12621263
case '\r':
1264+
if (MultilineString)
1265+
continue;
12631266
// Will be diagnosed as an unterminated string literal.
12641267
return CurPtr-1;
12651268

@@ -1272,12 +1275,12 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr,
12721275
}
12731276
// Otherwise it's an ordinary character; treat it normally.
12741277
} else {
1275-
if (*CurPtr == '"' && *(CurPtr + 1) == '"') {
1276-
Diags->diagnose(Lexer::getSourceLoc(CurPtr-1),
1277-
diag::lex_multiline_inside_interpolation);
1278-
}
12791278
OpenDelimiters.push_back(CurPtr[-1]);
12801279
}
1280+
if (*CurPtr == '"' && *(CurPtr + 1) == '"' && *(CurPtr - 1) == '"') {
1281+
MultilineString = true;
1282+
CurPtr += 2;
1283+
}
12811284
continue;
12821285
case '\\':
12831286
if (inStringLiteral()) {
@@ -1373,7 +1376,8 @@ static StringRef getMultilineTrailingIndent(const Token &Str,
13731376

13741377
/// validateMultilineIndents:
13751378
/// Diagnose contents of string literal that have inconsistent indentation.
1376-
void Lexer::validateMultilineIndents(const Token &Str) {
1379+
static void validateMultilineIndents(const Token &Str,
1380+
DiagnosticEngine *Diags) {
13771381
StringRef Indent = getMultilineTrailingIndent(Str, Diags);
13781382
if (Indent.empty())
13791383
return;
@@ -1385,7 +1389,8 @@ void Lexer::validateMultilineIndents(const Token &Str) {
13851389
size_t nextpos = pos + 1;
13861390
if (BytesPtr[nextpos] != '\n' && BytesPtr[nextpos] != '\r') {
13871391
if (Bytes.substr(nextpos, Indent.size()) != Indent)
1388-
diagnose(BytesPtr + nextpos, diag::lex_ambiguous_string_indent);
1392+
Diags->diagnose(Lexer::getSourceLoc(BytesPtr + nextpos),
1393+
diag::lex_ambiguous_string_indent);
13891394
}
13901395
pos = nextpos;
13911396
}
@@ -1415,7 +1420,8 @@ void Lexer::lexStringLiteral() {
14151420
// Consume tokens until we hit the corresponding ')'.
14161421
CurPtr += 2;
14171422
const char *EndPtr =
1418-
skipToEndOfInterpolatedExpression(CurPtr, BufferEnd, Diags);
1423+
skipToEndOfInterpolatedExpression(CurPtr, BufferEnd,
1424+
Diags, MultilineString);
14191425

14201426
if (*EndPtr == ')') {
14211427
// Successfully scanned the body of the expression literal.
@@ -1483,7 +1489,8 @@ void Lexer::lexStringLiteral() {
14831489
if (*CurPtr == '"' && *(CurPtr + 1) == '"' && *(CurPtr + 2) != '"') {
14841490
CurPtr += 2;
14851491
formToken(tok::string_literal, TokStart, MultilineString);
1486-
validateMultilineIndents(NextToken);
1492+
if (Diags)
1493+
validateMultilineIndents(NextToken, Diags);
14871494
return;
14881495
}
14891496
else
@@ -1778,7 +1785,7 @@ void Lexer::getStringLiteralSegments(
17781785
// Find the closing ')'.
17791786
const char *End = skipToEndOfInterpolatedExpression(BytesPtr,
17801787
Str.getText().end(),
1781-
Diags);
1788+
Diags, MultilineString);
17821789
assert(*End == ')' && "invalid string literal interpolations should"
17831790
" not be returned as string literals");
17841791
++End;

test/Parse/multiline_errors.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,3 @@ _ = """""" // expected-error@-0{{invalid start of multi-line string literal}}
4949

5050
_ = """ """ // expected-error@-0{{invalid start of multi-line string literal}}
5151
// newline currently required after opening """
52-
53-
_ = "\("""
54-
not valid
55-
""")" // expected-error@-2{{multi-line string literals are not allowed inside an interpolation}}
56-
// expected-error@-3{{unterminated string literal}}
57-
// expected-error@-2{{invalid start of multi-line string literal}}
58-
// expected-error@-3{{unterminated string literal}}

test/Parse/multiline_string.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,28 @@ _ = """
135135
_ = """
136136
"""
137137
// CHECK: ""
138+
139+
_ = "\("""
140+
\("a" + """
141+
valid
142+
""")
143+
""") literal"
144+
// CHECK: "a"
145+
// CHECK: " valid"
146+
// CHECK: " literal"
147+
148+
_ = "hello\("""
149+
world
150+
""")"
151+
// CHECK: "hello"
152+
// CHECK: "world"
153+
154+
_ = """
155+
hello\("""
156+
world
157+
""")
158+
abc
159+
"""
160+
// CHECK: "hello"
161+
// CHECK: "world"
162+
// CHECK: "\nabc"

0 commit comments

Comments
 (0)