Skip to content

Commit f9d27b9

Browse files
authored
Merge pull request #42214 from ahoppen/pr/parse-recover-no-lbrace
[Parser] When recovering from expression parsing don't stop at '{'
2 parents fb04b1c + 3bbffde commit f9d27b9

16 files changed

+94
-56
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,7 @@ ERROR(conditional_var_valid_identifiers_only,none,
10901090
ERROR(expected_condition_if,PointsToFirstBadToken,
10911091
"expected expression, var, or let in 'if' condition", ())
10921092
ERROR(missing_condition_after_if,none,
1093-
"missing condition in an 'if' statement", ())
1093+
"missing condition in 'if' statement", ())
10941094
ERROR(expected_lbrace_after_if,PointsToFirstBadToken,
10951095
"expected '{' after 'if' condition", ())
10961096
ERROR(expected_lbrace_or_if_after_else,PointsToFirstBadToken,
@@ -1106,7 +1106,7 @@ NOTE(suggest_removing_else,none,
11061106
ERROR(expected_condition_guard,PointsToFirstBadToken,
11071107
"expected expression, var, let or case in 'guard' condition", ())
11081108
ERROR(missing_condition_after_guard,none,
1109-
"missing condition in an 'guard' statement", ())
1109+
"missing condition in 'guard' statement", ())
11101110
ERROR(expected_else_after_guard,PointsToFirstBadToken,
11111111
"expected 'else' after 'guard' condition", ())
11121112
ERROR(expected_lbrace_after_guard,PointsToFirstBadToken,
@@ -1116,7 +1116,7 @@ ERROR(expected_lbrace_after_guard,PointsToFirstBadToken,
11161116
ERROR(expected_condition_while,PointsToFirstBadToken,
11171117
"expected expression, var, or let in 'while' condition", ())
11181118
ERROR(missing_condition_after_while,none,
1119-
"missing condition in a 'while' statement", ())
1119+
"missing condition in 'while' statement", ())
11201120
ERROR(expected_lbrace_after_while,PointsToFirstBadToken,
11211121
"expected '{' after 'while' condition", ())
11221122

include/swift/Parse/Parser.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -671,8 +671,15 @@ class Parser {
671671
/// skipUntilDeclStmtRBrace - Skip to the next decl or '}'.
672672
void skipUntilDeclRBrace();
673673

674-
void skipUntilDeclStmtRBrace(tok T1);
675-
void skipUntilDeclStmtRBrace(tok T1, tok T2);
674+
template <typename ...T>
675+
void skipUntilDeclStmtRBrace(T... K) {
676+
while (Tok.isNot(K..., tok::eof, tok::r_brace, tok::pound_endif,
677+
tok::pound_else, tok::pound_elseif,
678+
tok::code_complete) &&
679+
!isStartOfStmt() && !isStartOfSwiftDecl()) {
680+
skipSingle();
681+
}
682+
}
676683

677684
void skipUntilDeclRBrace(tok T1, tok T2);
678685

lib/Parse/ParseStmt.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -494,13 +494,12 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ASTNode> &Entries,
494494
if (NeedParseErrorRecovery) {
495495
SyntaxParsingContext TokenListCtxt(SyntaxContext,
496496
SyntaxKind::NonEmptyTokenList);
497-
// If we had a parse error, skip to the start of the next stmt, decl or
498-
// '{'.
497+
// If we had a parse error, skip to the start of the next stmt or decl.
499498
//
500499
// It would be ideal to stop at the start of the next expression (e.g.
501500
// "X = 4"), but distinguishing the start of an expression from the middle
502501
// of one is "hard".
503-
skipUntilDeclStmtRBrace(tok::l_brace);
502+
skipUntilDeclStmtRBrace();
504503

505504
// If we have to recover, pretend that we had a semicolon; it's less
506505
// noisy that way.
@@ -1875,7 +1874,7 @@ ParserResult<Stmt> Parser::parseStmtGuard() {
18751874
BraceStmt::create(Context, EndLoc, {}, EndLoc, /*implicit=*/true)));
18761875
};
18771876

1878-
if (Tok.is(tok::l_brace)) {
1877+
if (Tok.isAny(tok::l_brace, tok::kw_else)) {
18791878
SourceLoc LBraceLoc = Tok.getLoc();
18801879
diagnose(GuardLoc, diag::missing_condition_after_guard)
18811880
.highlight(SourceRange(GuardLoc, LBraceLoc));

lib/Parse/Parser.cpp

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -767,24 +767,6 @@ void Parser::skipUntilDeclRBrace() {
767767
skipSingle();
768768
}
769769

770-
void Parser::skipUntilDeclStmtRBrace(tok T1) {
771-
while (Tok.isNot(T1, tok::eof, tok::r_brace, tok::pound_endif,
772-
tok::pound_else, tok::pound_elseif,
773-
tok::code_complete) &&
774-
!isStartOfStmt() && !isStartOfSwiftDecl()) {
775-
skipSingle();
776-
}
777-
}
778-
779-
void Parser::skipUntilDeclStmtRBrace(tok T1, tok T2) {
780-
while (Tok.isNot(T1, T2, tok::eof, tok::r_brace, tok::pound_endif,
781-
tok::pound_else, tok::pound_elseif,
782-
tok::code_complete) &&
783-
!isStartOfStmt() && !isStartOfSwiftDecl()) {
784-
skipSingle();
785-
}
786-
}
787-
788770
void Parser::skipListUntilDeclRBrace(SourceLoc startLoc, tok T1, tok T2) {
789771
while (Tok.isNot(T1, T2, tok::eof, tok::r_brace, tok::pound_endif,
790772
tok::pound_else, tok::pound_elseif)) {

test/IDE/complete_cross_import.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ func foo() {
3434

3535
import #^IMPORT^#
3636

37+
func sync() {}
38+
3739
// IMPORT-DAG: Decl[Module]/None/NotRecommended: A[#Module#]; name=A
3840
// IMPORT-DAG: Decl[Module]/None/NotRecommended: B[#Module#]; name=B
3941
// IMPORT-DAG: Decl[Module]/None: C[#Module#]; name=C

test/IDE/complete_in_result_builder.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,49 @@ func testCompleteErrorTypeInCatch() {
172172
// CATHC2-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E2({#Int32#})[#Error4#]; name=E2(Int32)
173173
// CATCH2: End completions
174174
}
175+
176+
func testCompleteInStringLiteral() {
177+
struct Island {
178+
var turnipPrice: String
179+
}
180+
181+
func takeTrailingClosure(_ x: () -> Void) -> Text { fatalError() }
182+
183+
struct BStack<Content> {
184+
init(@ViewBuilder content: () -> Content) {}
185+
}
186+
187+
protocol View {}
188+
189+
struct Text: View {
190+
init(_ x: String) {}
191+
192+
var body: Never { fatalError() }
193+
}
194+
195+
@resultBuilder struct ViewBuilder {
196+
static func buildBlock() -> Text { fatalError() }
197+
static func buildBlock<C: View>(_ c: C) -> C { return c }
198+
static func buildBlock<C1: View, C2: View>(_ c: C1, _ d: C2) -> C1 { return c }
199+
}
200+
201+
202+
func foo(island: Island) {
203+
BStack {
204+
let b = "\(island.#^STRING_LITERAL_VAR^#turnipPrice)"
205+
takeTrailingClosure {}
206+
}
207+
// STRING_LITERAL_VAR: Begin completions, 2 items
208+
// STRING_LITERAL_VAR-DAG: Keyword[self]/CurrNominal: self[#Island#]; name=self
209+
// STRING_LITERAL_VAR-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: turnipPrice[#String#]; name=turnipPrice
210+
// STRING_LITERAL_VAR: End completions
211+
212+
213+
func bar(island: Island) {
214+
BStack {
215+
Text("\(island.#^STRING_LITERAL_AS_ARGUMENT?check=STRING_LITERAL_VAR^#turnipPrice)")
216+
takeTrailingClosure {}
217+
}
218+
}
219+
220+
}

test/Parse/availability_query.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ if #available(OSX 10.51, *) && #available(OSX 10.52, *) { // expected-error {{ex
2121
}
2222

2323

24-
if #available { // expected-error {{expected availability condition}} expected-error {{closure expression is unused}} expected-error {{top-level statement cannot begin with a closure expression}} expected-note {{did you mean to use a 'do' statement?}} {{15-15=do }}
24+
if #available { // expected-error {{expected availability condition}}
2525
}
2626

2727
if #available( { // expected-error {{expected platform name}} expected-error {{expected ')'}} expected-note {{to match this opening '('}}

test/Parse/availability_query_unavailability.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ if #unavailable(OSX 10.51) && #unavailable(OSX 10.52) { // expected-error {{expe
1919
}
2020

2121

22-
if #unavailable { // expected-error {{expected availability condition}} expected-error {{closure expression is unused}} expected-error {{top-level statement cannot begin with a closure expression}} expected-note {{did you mean to use a 'do' statement?}} {{17-17=do }}
22+
if #unavailable { // expected-error {{expected availability condition}}
2323
}
2424

2525
if #unavailable( { // expected-error {{expected platform name}} expected-error {{expected ')'}} expected-note {{to match this opening '('}}
@@ -124,4 +124,4 @@ if #available(macOS 10, *) {
124124
if #available(*) == false { // expected-error {{#available cannot be used as an expression, did you mean to use '#unavailable'?}} {{4-14=#unavailable}} {{18-27=}}
125125
}
126126
if !#available(*) { // expected-error {{#available cannot be used as an expression, did you mean to use '#unavailable'?}} {{4-15=#unavailable}}
127-
}
127+
}

test/Parse/guard.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
func noConditionNoElse() {
4+
guard {} // expected-error {{missing condition in 'guard' statement}} expected-error {{expected 'else' after 'guard' condition}}
5+
}
6+
func noCondition() {
7+
guard else {} // expected-error {{missing condition in 'guard' statement}}
8+
}

test/Parse/recovery.swift

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -79,60 +79,60 @@ class ClassWithStaticDecls {
7979
func missingControllingExprInIf() {
8080
if // expected-error {{expected expression, var, or let in 'if' condition}}
8181

82-
if { // expected-error {{missing condition in an 'if' statement}}
82+
if { // expected-error {{missing condition in 'if' statement}}
8383
}
8484

85-
if // expected-error {{missing condition in an 'if' statement}}
85+
if // expected-error {{missing condition in 'if' statement}}
8686
{
8787
}
8888

8989
if true {
90-
} else if { // expected-error {{missing condition in an 'if' statement}}
90+
} else if { // expected-error {{missing condition in 'if' statement}}
9191
}
9292

9393
// It is debatable if we should do recovery here and parse { true } as the
9494
// body, but the error message should be sensible.
95-
if { true } { // expected-error {{missing condition in an 'if' statement}} expected-error{{consecutive statements on a line must be separated by ';'}} {{14-14=;}} expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{15-15=do }} expected-warning {{boolean literal is unused}}
95+
if { true } { // expected-error {{missing condition in 'if' statement}} expected-error{{consecutive statements on a line must be separated by ';'}} {{14-14=;}} expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{15-15=do }} expected-warning {{boolean literal is unused}}
9696
}
9797

98-
if { true }() { // expected-error {{missing condition in an 'if' statement}} expected-error 2 {{consecutive statements on a line must be separated by ';'}} expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{17-17=do }} expected-warning {{boolean literal is unused}}
98+
if { true }() { // expected-error {{missing condition in 'if' statement}} expected-error 2 {{consecutive statements on a line must be separated by ';'}} expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{17-17=do }} expected-warning {{boolean literal is unused}}
9999
}
100100

101101
// <rdar://problem/18940198>
102-
if { { } } // expected-error{{missing condition in an 'if' statement}} expected-error{{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{8-8=do }}
102+
if { { } } // expected-error{{missing condition in 'if' statement}} expected-error{{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{8-8=do }}
103103
}
104104

105105
func missingControllingExprInWhile() {
106106
while // expected-error {{expected expression, var, or let in 'while' condition}}
107107

108-
while { // expected-error {{missing condition in a 'while' statement}}
108+
while { // expected-error {{missing condition in 'while' statement}}
109109
}
110110

111-
while // expected-error {{missing condition in a 'while' statement}}
111+
while // expected-error {{missing condition in 'while' statement}}
112112
{
113113
}
114114

115115
// It is debatable if we should do recovery here and parse { true } as the
116116
// body, but the error message should be sensible.
117-
while { true } { // expected-error {{missing condition in a 'while' statement}} expected-error{{consecutive statements on a line must be separated by ';'}} {{17-17=;}} expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{18-18=do }} expected-warning {{boolean literal is unused}}
117+
while { true } { // expected-error {{missing condition in 'while' statement}} expected-error{{consecutive statements on a line must be separated by ';'}} {{17-17=;}} expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{18-18=do }} expected-warning {{boolean literal is unused}}
118118
}
119119

120-
while { true }() { // expected-error {{missing condition in a 'while' statement}} expected-error 2 {{consecutive statements on a line must be separated by ';'}} expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{20-20=do }} expected-warning {{boolean literal is unused}}
120+
while { true }() { // expected-error {{missing condition in 'while' statement}} expected-error 2 {{consecutive statements on a line must be separated by ';'}} expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{20-20=do }} expected-warning {{boolean literal is unused}}
121121
}
122122

123123
// <rdar://problem/18940198>
124-
while { { } } // expected-error{{missing condition in a 'while' statement}} expected-error{{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{11-11=do }}
124+
while { { } } // expected-error{{missing condition in 'while' statement}} expected-error{{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{11-11=do }}
125125
}
126126

127127
func missingControllingExprInRepeatWhile() {
128128
repeat {
129-
} while // expected-error {{missing condition in a 'while' statement}}
129+
} while // expected-error {{missing condition in 'while' statement}}
130130
{ // expected-error{{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} {{3-3=do }}
131131
missingControllingExprInRepeatWhile();
132132
}
133133

134134
repeat {
135-
} while { true }() // expected-error{{missing condition in a 'while' statement}} expected-error{{consecutive statements on a line must be separated by ';'}} {{10-10=;}} expected-warning {{result of call to closure returning 'Bool' is unused}}
135+
} while { true }() // expected-error{{missing condition in 'while' statement}} expected-error{{consecutive statements on a line must be separated by ';'}} {{10-10=;}} expected-warning {{result of call to closure returning 'Bool' is unused}}
136136
}
137137

138138
// SR-165
@@ -660,9 +660,6 @@ func foo2(bar! = baz) {}// expected-note {{did you mean 'foo2'?}}
660660
// expected-error@+1{{cannot find 'esp' in scope; did you mean 'test'?}}
661661
switch esp {
662662
case let (jeb):
663-
// expected-error@+5{{top-level statement cannot begin with a closure expression}}
664-
// expected-error@+4{{closure expression is unused}}
665-
// expected-note@+3{{did you mean to use a 'do' statement?}}
666663
// expected-error@+2{{expected an identifier to name generic parameter}}
667664
// expected-error@+1{{expected '{' in class}}
668665
class Ceac<}> {}

test/Parse/switch.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ func ~= (x: (Int,Int), y: (Int,Int)) -> Bool {
77
}
88

99
func parseError1(x: Int) {
10-
switch func {} // expected-error {{expected expression in 'switch' statement}} expected-error {{expected identifier in function declaration}} expected-error {{closure expression is unused}} expected-note{{did you mean to use a 'do' statement?}} {{15-15=do }}
10+
switch func {} // expected-error {{expected expression in 'switch' statement}} expected-error {{expected identifier in function declaration}}
1111
}
1212

1313
func parseError2(x: Int) {

test/Parse/trailing_closures.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ func produce(fn: () -> Int?, default d: () -> Int) -> Int { // expected-note {{d
108108
return fn() ?? d()
109109
}
110110
// TODO: The diagnostics here are perhaps a little overboard.
111-
_ = produce { 0 } default: { 1 } // expected-error {{missing argument for parameter 'default' in call}} expected-error {{consecutive statements}} expected-error {{'default' label can only appear inside a 'switch' statement}} expected-error {{top-level statement cannot begin with a closure expression}} expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}}
112-
_ = produce { 2 } `default`: { 3 }
111+
_ = produce { 0 } default: { 1 } // expected-error {{missing argument for parameter 'default' in call}} expected-error {{consecutive statements}} expected-error {{'default' label can only appear inside a 'switch' statement}}
112+
_ = produce { 2 } `default`: { 3 } // expected-error {{labeled block needs 'do'}} expected-warning {{integer literal is unused}}
113113

114114
func f() -> Int { 42 }
115115

test/Syntax/diagnostics_verify.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ if false {
1414

1515
class { // expected-error {{unknown declaration syntax exists in the source}}
1616
// expected-error@-1 {{expected identifier in class declaration}}
17-
// expected-note@-2 {{did you mean to use a 'do' statement?}}
18-
// expected-error@-3 {{closure expression is unused}}
19-
// expected-error@-4 {{top-level statement cannot begin with a closure expression}}
2017

2118
}
2219

test/decl/func/operator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ prefix func +// this should be a comment, not an operator
8282
prefix func -/* this also should be a comment, not an operator */
8383
(arg: Int) -> Int { return arg }
8484

85-
func +*/ () {} // expected-error {{expected identifier in function declaration}} expected-error {{unexpected end of block comment}} expected-error {{closure expression is unused}} expected-error{{top-level statement cannot begin with a closure expression}} expected-note{{did you mean to use a 'do' statement?}} {{13-13=do }}
85+
func +*/ () {} // expected-error {{expected identifier in function declaration}} expected-error {{unexpected end of block comment}}
8686
func errors() {
8787
*/ // expected-error {{unexpected end of block comment}}
8888

test/decl/func/static_func.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ static override func gf5() {} // expected-error {{static methods may only be dec
1313
class override func gf6() {} // expected-error {{class methods may only be declared on a type}}{{1-7=}}
1414
// expected-error@-1 {{'override' can only be specified on class members}}{{7-16=}}
1515

16-
static gf7() {} // expected-error {{expected declaration}} expected-error {{closure expression is unused}} expected-error{{begin with a closure}} expected-note{{did you mean to use a 'do' statement?}} {{14-14=do }}
17-
class gf8() {} // expected-error {{expected '{' in class}} expected-error {{closure expression is unused}} expected-error{{begin with a closure}} expected-note{{did you mean to use a 'do' statement?}} {{13-13=do }}
16+
static gf7() {} // expected-error {{expected declaration}}
17+
class gf8() {} // expected-error {{expected '{' in class}}
1818

1919
func inGlobalFunc() {
2020
static func gf1() {} // expected-error {{static methods may only be declared on a type}}{{3-10=}}

test/stmt/if_while_var.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ if 1 != 2, 4 == 57, let x = opt {} // expected-warning {{immutable value 'x' was
136136

137137
// Test that these don't cause the parser to crash.
138138
if true { if a == 0; {} } // expected-error {{cannot find 'a' in scope}} expected-error {{expected '{' after 'if' condition}}
139-
if a == 0, where b == 0 {} // expected-error 4{{}} expected-note {{}} {{25-25=do }}
139+
if a == 0, where b == 0 {} // expected-error {{cannot find 'a' in scope}} expected-error {{expected expression in conditional}}
140140

141141

142142

0 commit comments

Comments
 (0)