Merge pull request #10699 from rintaro/4.0-lexer-multiline-interpolation
[4.0][Parse] Fix skipping string interpolation in Lexer
diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp
index 7744930..9170c23 100644
--- a/lib/Parse/Lexer.cpp
+++ b/lib/Parse/Lexer.cpp
@@ -1244,6 +1244,9 @@
DiagnosticEngine *Diags,
bool MultilineString) {
llvm::SmallVector<char, 4> OpenDelimiters;
+ llvm::SmallVector<bool, 4> AllowNewline;
+ AllowNewline.push_back(MultilineString);
+
auto inStringLiteral = [&]() {
return !OpenDelimiters.empty() &&
(OpenDelimiters.back() == '"' || OpenDelimiters.back() == '\'');
@@ -1262,27 +1265,46 @@
// interpolated ones are no exception - unless multiline literals.
case '\n':
case '\r':
- if (MultilineString)
+ if (AllowNewline.back())
continue;
// Will be diagnosed as an unterminated string literal.
return CurPtr-1;
case '"':
- case '\'':
- if (inStringLiteral()) {
- // Is it the closing quote?
+ case '\'': {
+ if (!AllowNewline.back() && inStringLiteral()) {
if (OpenDelimiters.back() == CurPtr[-1]) {
+ // Closing single line string literal.
OpenDelimiters.pop_back();
+ AllowNewline.pop_back();
}
- // Otherwise it's an ordinary character; treat it normally.
- } else {
- OpenDelimiters.push_back(CurPtr[-1]);
+ // Otherwise, it's just a quote in string literal. e.g. "foo's".
+ continue;
}
- if (*CurPtr == '"' && *(CurPtr + 1) == '"' && *(CurPtr - 1) == '"') {
- MultilineString = true;
+
+ bool isMultilineQuote = (
+ *CurPtr == '"' && *(CurPtr + 1) == '"' && *(CurPtr - 1) == '"');
+ if (isMultilineQuote)
CurPtr += 2;
+
+ if (!inStringLiteral()) {
+ // Open string literal
+ OpenDelimiters.push_back(CurPtr[-1]);
+ AllowNewline.push_back(isMultilineQuote);
+ continue;
}
+
+ // We are in multiline string literal.
+ assert(AllowNewline.back() && "other cases must be handled above");
+ if (isMultilineQuote) {
+ // Close multiline string literal.
+ OpenDelimiters.pop_back();
+ AllowNewline.pop_back();
+ }
+
+ // Otherwise, it's just a normal character in multiline string.
continue;
+ }
case '\\':
if (inStringLiteral()) {
char escapedChar = *CurPtr++;
diff --git a/test/Parse/multiline_errors.swift b/test/Parse/multiline_errors.swift
index e58b64b..21275ee 100644
--- a/test/Parse/multiline_errors.swift
+++ b/test/Parse/multiline_errors.swift
@@ -105,4 +105,18 @@
// expected-note@-15{{change indentation of these lines to match closing delimiter}} {{2-2= }} {{2-2= }}
// expected-error@-14{{unexpected space in indentation of next 4 lines in multi-line string literal}}
// expected-note@-7{{should match tab here}}
- // expected-note@-16{{change indentation of these lines to match closing delimiter}} {{1-1= }} {{1-1= }} {{1-1= }} {{1-1= }}
\ No newline at end of file
+ // expected-note@-16{{change indentation of these lines to match closing delimiter}} {{1-1= }} {{1-1= }} {{1-1= }} {{1-1= }}
+
+_ = "hello\("""
+ world
+ """
+ )!"
+ // expected-error@-4 {{unterminated string literal}}
+ // expected-error@-2 {{unterminated string literal}}
+
+_ = "hello\(
+ """
+ world
+ """)!"
+ // expected-error@-4 {{unterminated string literal}}
+ // expected-error@-2 {{unterminated string literal}}
diff --git a/test/Parse/multiline_string.swift b/test/Parse/multiline_string.swift
index fc0e36a..03020c2 100644
--- a/test/Parse/multiline_string.swift
+++ b/test/Parse/multiline_string.swift
@@ -160,3 +160,24 @@
// CHECK: "hello"
// CHECK: "world"
// CHECK: "\nabc"
+
+_ = "hello\("""
+ "world'
+ """)abc"
+// CHECK: "hello"
+// CHECK: "\"world'"
+// CHECK: "abc"
+
+_ = """
+ welcome
+ \(
+ "to\("""
+ Swift
+ """)"
+ )
+ !
+ """
+// CHECK: "welcome\n"
+// CHECK: "to"
+// CHECK: "Swift"
+// CHECK: "\n!"