Skip to content

Commit c8bd1aa

Browse files
committed
[Parse] Fix skipping string interpolation in Lexer
Maintain inner most string literal mode to determine whether we allow newline character or not. * Disallow newline after multiline string in string interpolation. (SR-5171) * Allow unbalanced `"` in multiline string in string interpolation.
1 parent 4b0597a commit c8bd1aa

File tree

3 files changed

+67
-10
lines changed

3 files changed

+67
-10
lines changed

lib/Parse/Lexer.cpp

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,9 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr,
12441244
DiagnosticEngine *Diags,
12451245
bool MultilineString) {
12461246
llvm::SmallVector<char, 4> OpenDelimiters;
1247+
llvm::SmallVector<bool, 4> AllowNewline;
1248+
AllowNewline.push_back(MultilineString);
1249+
12471250
auto inStringLiteral = [&]() {
12481251
return !OpenDelimiters.empty() &&
12491252
(OpenDelimiters.back() == '"' || OpenDelimiters.back() == '\'');
@@ -1262,27 +1265,46 @@ static const char *skipToEndOfInterpolatedExpression(const char *CurPtr,
12621265
// interpolated ones are no exception - unless multiline literals.
12631266
case '\n':
12641267
case '\r':
1265-
if (MultilineString)
1268+
if (AllowNewline.back())
12661269
continue;
12671270
// Will be diagnosed as an unterminated string literal.
12681271
return CurPtr-1;
12691272

12701273
case '"':
1271-
case '\'':
1272-
if (inStringLiteral()) {
1273-
// Is it the closing quote?
1274+
case '\'': {
1275+
if (!AllowNewline.back() && inStringLiteral()) {
12741276
if (OpenDelimiters.back() == CurPtr[-1]) {
1277+
// Closing single line string literal.
12751278
OpenDelimiters.pop_back();
1279+
AllowNewline.pop_back();
12761280
}
1277-
// Otherwise it's an ordinary character; treat it normally.
1278-
} else {
1279-
OpenDelimiters.push_back(CurPtr[-1]);
1281+
// Otherwise, it's just a quote in string literal. e.g. "foo's".
1282+
continue;
12801283
}
1281-
if (*CurPtr == '"' && *(CurPtr + 1) == '"' && *(CurPtr - 1) == '"') {
1282-
MultilineString = true;
1284+
1285+
bool isMultilineQuote = (
1286+
*CurPtr == '"' && *(CurPtr + 1) == '"' && *(CurPtr - 1) == '"');
1287+
if (isMultilineQuote)
12831288
CurPtr += 2;
1289+
1290+
if (!inStringLiteral()) {
1291+
// Open string literal
1292+
OpenDelimiters.push_back(CurPtr[-1]);
1293+
AllowNewline.push_back(isMultilineQuote);
1294+
continue;
12841295
}
1296+
1297+
// We are in multiline string literal.
1298+
assert(AllowNewline.back() && "other cases must be handled above");
1299+
if (isMultilineQuote) {
1300+
// Close multiline string literal.
1301+
OpenDelimiters.pop_back();
1302+
AllowNewline.pop_back();
1303+
}
1304+
1305+
// Otherwise, it's just a normal character in multiline string.
12851306
continue;
1307+
}
12861308
case '\\':
12871309
if (inStringLiteral()) {
12881310
char escapedChar = *CurPtr++;

test/Parse/multiline_errors.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,18 @@ Three B
105105
// expected-note@-15{{change indentation of these lines to match closing delimiter}} {{2-2= }} {{2-2= }}
106106
// expected-error@-14{{unexpected space in indentation of next 4 lines in multi-line string literal}}
107107
// expected-note@-7{{should match tab here}}
108-
// expected-note@-16{{change indentation of these lines to match closing delimiter}} {{1-1= }} {{1-1= }} {{1-1= }} {{1-1= }}
108+
// expected-note@-16{{change indentation of these lines to match closing delimiter}} {{1-1= }} {{1-1= }} {{1-1= }} {{1-1= }}
109+
110+
_ = "hello\("""
111+
world
112+
"""
113+
)!"
114+
// expected-error@-4 {{unterminated string literal}}
115+
// expected-error@-2 {{unterminated string literal}}
116+
117+
_ = "hello\(
118+
"""
119+
world
120+
""")!"
121+
// expected-error@-4 {{unterminated string literal}}
122+
// expected-error@-2 {{unterminated string literal}}

test/Parse/multiline_string.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,24 @@ _ = """
160160
// CHECK: "hello"
161161
// CHECK: "world"
162162
// CHECK: "\nabc"
163+
164+
_ = "hello\("""
165+
"world'
166+
""")abc"
167+
// CHECK: "hello"
168+
// CHECK: "\"world'"
169+
// CHECK: "abc"
170+
171+
_ = """
172+
welcome
173+
\(
174+
"to\("""
175+
Swift
176+
""")"
177+
)
178+
!
179+
"""
180+
// CHECK: "welcome\n"
181+
// CHECK: "to"
182+
// CHECK: "Swift"
183+
// CHECK: "\n!"

0 commit comments

Comments
 (0)