Skip to content

Commit e05f68c

Browse files
authored
Change diagnostic error thrown for when string interpolations aren't closed by a parenthesis (#58882)
[Parse] Diagnose unclosed string interpolations
1 parent 3b849e0 commit e05f68c

10 files changed

+81
-24
lines changed

include/swift/AST/DiagnosticsParse.def

+5
Original file line numberDiff line numberDiff line change
@@ -1353,6 +1353,11 @@ ERROR(expected_type_after_as,none,
13531353
ERROR(string_interpolation_extra,none,
13541354
"extra tokens after interpolated string expression", ())
13551355

1356+
// String interpolation isnt closed by a )
1357+
// ie: `let west = "ye \("`
1358+
ERROR(string_interpolation_unclosed, none,
1359+
"cannot find ')' to match opening '(' in string interpolation", ())
1360+
13561361
// Interpolations with parameter labels or multiple values
13571362
WARNING(string_interpolation_list_changing,none,
13581363
"interpolating multiple values will not form a tuple in Swift 5", ())

lib/Parse/Lexer.cpp

+14-6
Original file line numberDiff line numberDiff line change
@@ -1884,13 +1884,21 @@ void Lexer::lexStringLiteral(unsigned CustomDelimiterLen) {
18841884
// Successfully scanned the body of the expression literal.
18851885
++CurPtr;
18861886
continue;
1887-
} else if ((*CurPtr == '\r' || *CurPtr == '\n') && IsMultilineString) {
1888-
// The only case we reach here is unterminated single line string in the
1889-
// interpolation. For better recovery, go on after emitting an error.
1890-
diagnose(CurPtr, diag::lex_unterminated_string);
1891-
wasErroneous = true;
1892-
continue;
18931887
} else {
1888+
if ((*CurPtr == '\r' || *CurPtr == '\n') && IsMultilineString) {
1889+
diagnose(--TmpPtr, diag::string_interpolation_unclosed);
1890+
1891+
// The only case we reach here is unterminated single line string in
1892+
// the interpolation. For better recovery, go on after emitting
1893+
// an error.
1894+
diagnose(CurPtr, diag::lex_unterminated_string);
1895+
wasErroneous = true;
1896+
continue;
1897+
} else if (!IsMultilineString || CurPtr == BufferEnd) {
1898+
diagnose(--TmpPtr, diag::string_interpolation_unclosed);
1899+
}
1900+
1901+
// As a fallback, just emit an unterminated string error.
18941902
diagnose(TokStart, diag::lex_unterminated_string);
18951903
return formToken(tok::unknown, TokStart);
18961904
}

test/Parse/multiline_errors.swift

+9-5
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,17 @@ _ = "hello\("""
126126
world
127127
"""
128128
)!"
129-
// expected-error@-4 {{unterminated string literal}}
130-
// expected-error@-2 {{unterminated string literal}}
129+
// expected-error@-4 {{cannot find ')' to match opening '(' in string interpolation}}
130+
// expected-error@-5 {{unterminated string literal}}
131+
// expected-error@-3 {{unterminated string literal}}
131132

132133
_ = "hello\(
133134
"""
134135
world
135136
""")!"
136-
// expected-error@-4 {{unterminated string literal}}
137-
// expected-error@-2 {{unterminated string literal}}
137+
// expected-error@-4 {{cannot find ')' to match opening '(' in string interpolation}}
138+
// expected-error@-5 {{unterminated string literal}}
139+
// expected-error@-3 {{unterminated string literal}}
138140

139141
_ = """
140142
line one \ non-whitespace
@@ -190,4 +192,6 @@ let _ = """
190192
\("bar
191193
baz
192194
"""
193-
// expected-error@-3 {{unterminated string literal}}
195+
// expected-error@-3 {{cannot find ')' to match opening '(' in string interpolation}}
196+
// expected-error@-4 {{unterminated string literal}}
197+

test/Parse/string_literal_eof1.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// RUN: %target-typecheck-verify-swift
22

33
// NOTE: DO NOT add a newline at EOF.
4-
// expected-error@+1 {{unterminated string literal}}
4+
// expected-error@+2 {{unterminated string literal}}
5+
// expected-error@+1 {{cannot find ')' to match opening '(' in string interpolation}}
56
_ = "foo\(

test/Parse/string_literal_eof2.swift

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// RUN: %target-typecheck-verify-swift
22

33
// NOTE: DO NOT add a newline at EOF.
4+
// expected-error@+2 {{cannot find ')' to match opening '(' in string interpolation}}
45
// expected-error@+1 {{unterminated string literal}}
56
_ = "foo\("bar

test/Parse/string_literal_eof5.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// RUN: %target-typecheck-verify-swift
22

33
// NOTE: DO NOT add a newline at EOF.
4-
// expected-error@+1 {{unterminated string literal}}
4+
// expected-error@+2 {{unterminated string literal}}
5+
// expected-error@+3 {{cannot find ')' to match opening '(' in string interpolation}}
56
_ = """
67
foo
78
\(

test/Parse/string_literal_eof6.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// RUN: %target-typecheck-verify-swift
22

33
// NOTE: DO NOT add a newline at EOF.
4-
// expected-error@+1 {{unterminated string literal}}
4+
// expected-error@+2 {{unterminated string literal}}
5+
// expected-error@+3 {{cannot find ')' to match opening '(' in string interpolation}}
56
_ = """
67
foo
78
\("bar

test/Parse/string_literal_eof7.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %target-typecheck-verify-swift
22

3-
// expected-error@+4 {{unterminated string literal}}
4-
// expected-error@+1 {{unterminated string literal}}
3+
// expected-error@+4 {{cannot find ')' to match opening '(' in string interpolation}}
4+
// expected-error@+1 {{unterminated string literal}} expected-error@+3 {{unterminated string literal}}
55
_ = """
66
foo
77
\("bar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
let mid = "pete"
4+
5+
_ = "mid == \(pete"
6+
// expected-error @-1 {{cannot find ')' to match opening '(' in string interpolation}}
7+
// expected-error @-2 {{unterminated string literal}}
8+
9+
let theGoat = "kanye \("
10+
// expected-error @-1 {{cannot find ')' to match opening '(' in string interpolation}}
11+
// expected-error @-2 {{unterminated string literal}}
12+
13+
let equation1 = "2 + 2 = \(2 + 2"
14+
// expected-error @-1 {{cannot find ')' to match opening '(' in string interpolation}}
15+
// expected-error @-2 {{unterminated string literal}}
16+
17+
let s = "\(x"; print(x)
18+
// expected-error @-1 {{cannot find ')' to match opening '(' in string interpolation}}
19+
// expected-error @-2 {{unterminated string literal}}
20+
21+
let zzz = "\(x; print(x)
22+
// expected-error @-1 {{cannot find ')' to match opening '(' in string interpolation}}
23+
// expected-error @-2 {{unterminated string literal}}
24+
25+
let goatedAlbum = "The Life Of \("Pablo"
26+
// expected-error @-1 {{cannot find ')' to match opening '(' in string interpolation}}
27+
// expected-error @-2 {{unterminated string literal}}
28+
29+
// expected-error @+3 {{cannot find ')' to match opening '(' in string interpolation}}
30+
// expected-error @+1 {{unterminated string literal}}
31+
_ = """
32+
\(
33+
"""

test/expr/expressions.swift

+11-8
Original file line numberDiff line numberDiff line change
@@ -475,9 +475,11 @@ func stringliterals(_ d: [String: Int]) {
475475
let x = 4
476476
"Hello \(x+1) world" // expected-warning {{string literal is unused}}
477477

478-
"Error: \(x+1"; // expected-error {{unterminated string literal}}
479-
480-
"Error: \(x+1 // expected-error {{unterminated string literal}}
478+
// expected-error @+1 {{unterminated string literal}}
479+
"Error: \(x+1"; // expected-error {{cannot find ')' to match opening '(' in string interpolation}}
480+
481+
// expected-error @+1 {{unterminated string literal}}
482+
"Error: \(x+1 // expected-error {{cannot find ')' to match opening '(' in string interpolation}}
481483
; // expected-error {{';' statements are not allowed}}
482484

483485
// rdar://14050788 [DF] String Interpolations can't contain quotes
@@ -488,15 +490,16 @@ func stringliterals(_ d: [String: Int]) {
488490
"test \("quoted-paren (")"
489491
"test \("\\")"
490492
"test \("\n")"
491-
"test \("\")" // expected-error {{unterminated string literal}}
493+
"test \("\")" // expected-error {{cannot find ')' to match opening '(' in string interpolation}} expected-error {{unterminated string literal}}
492494

493495
"test \
494496
// expected-error @-1 {{unterminated string literal}} expected-error @-1 {{invalid escape sequence in literal}}
495497
"test \("\
496-
// expected-error @-1 {{unterminated string literal}}
498+
// expected-error @-1 {{cannot find ')' to match opening '(' in string interpolation}} expected-error @-1 {{unterminated string literal}}
497499
"test newline \("something" +
498500
"something else")"
499-
// expected-error @-2 {{unterminated string literal}} expected-error @-1 {{unterminated string literal}}
501+
// expected-error @-2 {{cannot find ')' to match opening '(' in string interpolation}}
502+
// expected-error @-2 {{unterminated string literal}} expected-error @-3 {{unterminated string literal}}
500503

501504
// expected-warning @+2 {{variable 'x2' was never used; consider replacing with '_' or removing it}}
502505
// expected-error @+1 {{unterminated string literal}}
@@ -939,10 +942,10 @@ let _ = 0xFFF_FFFF_FFFF_FFFF as Int64
939942

940943
// rdar://problem/20289969 - string interpolation with comment containing ')' or '"'
941944
let _ = "foo \(42 /* ) " ) */)"
942-
let _ = "foo \(foo // ) " // expected-error {{unterminated string literal}}
945+
let _ = "foo \(foo // ) " // expected-error {{cannot find ')' to match opening '(' in string interpolation}} expected-error {{unterminated string literal}}
943946
let _ = "foo \(42 /*
944947
* multiline comment
945948
*/)end"
946-
// expected-error @-3 {{unterminated string literal}}
949+
// expected-error @-3 {{cannot find ')' to match opening '(' in string interpolation}} expected-error @-3 {{unterminated string literal}}
947950
// expected-error @-2 {{expected expression}}
948951
// expected-error @-3 {{unterminated string literal}}

0 commit comments

Comments
 (0)