[Parser] When recovering from expression parsing don't stop at '{'

When recovering from a parser error in an expression, we resumed parsing at a '{'. I assume this was because we wanted to continue inside e.g. an if-body if parsing the condition failed, but it's actually causing more issue because when parsing e.g.

```swift
expr + has - error +

functionTakesClosure {
}
```

we continue parsing at the `{` of the trailing closure, which is a completely garbage location to continue parsing.

The motivating example for this change was (in a result builder)
```swift
Text("\(island.#^COMPLETE^#)")
takeTrailingClosure {}
```

Here `Text(…)` has an error (because it contains a code completion token) and thus we skip `takeTrailingClosure`, effectively parsing
```swift
Text(….) {}
```

which the type checker wasn’t very happy with and thus refused to provide code completion. With this change, we completely drop `takeTrailingClosure {}`. The type checker is a lot happier with that.
This commit is contained in:
Alex Hoppen
2022-04-07 09:15:54 +02:00
parent 1e1d4e3714
commit bfc68f48e4
14 changed files with 70 additions and 40 deletions

View File

@@ -671,8 +671,15 @@ public:
/// skipUntilDeclStmtRBrace - Skip to the next decl or '}'.
void skipUntilDeclRBrace();
void skipUntilDeclStmtRBrace(tok T1);
void skipUntilDeclStmtRBrace(tok T1, tok T2);
template <typename ...T>
void skipUntilDeclStmtRBrace(T... K) {
while (Tok.isNot(K..., tok::eof, tok::r_brace, tok::pound_endif,
tok::pound_else, tok::pound_elseif,
tok::code_complete) &&
!isStartOfStmt() && !isStartOfSwiftDecl()) {
skipSingle();
}
}
void skipUntilDeclRBrace(tok T1, tok T2);

View File

@@ -494,13 +494,12 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ASTNode> &Entries,
if (NeedParseErrorRecovery) {
SyntaxParsingContext TokenListCtxt(SyntaxContext,
SyntaxKind::NonEmptyTokenList);
// If we had a parse error, skip to the start of the next stmt, decl or
// '{'.
// If we had a parse error, skip to the start of the next stmt or decl.
//
// It would be ideal to stop at the start of the next expression (e.g.
// "X = 4"), but distinguishing the start of an expression from the middle
// of one is "hard".
skipUntilDeclStmtRBrace(tok::l_brace);
skipUntilDeclStmtRBrace();
// If we have to recover, pretend that we had a semicolon; it's less
// noisy that way.
@@ -1875,7 +1874,7 @@ ParserResult<Stmt> Parser::parseStmtGuard() {
BraceStmt::create(Context, EndLoc, {}, EndLoc, /*implicit=*/true)));
};
if (Tok.is(tok::l_brace)) {
if (Tok.isAny(tok::l_brace, tok::kw_else)) {
SourceLoc LBraceLoc = Tok.getLoc();
diagnose(GuardLoc, diag::missing_condition_after_guard)
.highlight(SourceRange(GuardLoc, LBraceLoc));

View File

@@ -767,24 +767,6 @@ void Parser::skipUntilDeclRBrace() {
skipSingle();
}
void Parser::skipUntilDeclStmtRBrace(tok T1) {
while (Tok.isNot(T1, tok::eof, tok::r_brace, tok::pound_endif,
tok::pound_else, tok::pound_elseif,
tok::code_complete) &&
!isStartOfStmt() && !isStartOfSwiftDecl()) {
skipSingle();
}
}
void Parser::skipUntilDeclStmtRBrace(tok T1, tok T2) {
while (Tok.isNot(T1, T2, tok::eof, tok::r_brace, tok::pound_endif,
tok::pound_else, tok::pound_elseif,
tok::code_complete) &&
!isStartOfStmt() && !isStartOfSwiftDecl()) {
skipSingle();
}
}
void Parser::skipListUntilDeclRBrace(SourceLoc startLoc, tok T1, tok T2) {
while (Tok.isNot(T1, T2, tok::eof, tok::r_brace, tok::pound_endif,
tok::pound_else, tok::pound_elseif)) {

View File

@@ -34,6 +34,8 @@ func foo() {
import #^IMPORT^#
func sync() {}
// IMPORT-DAG: Decl[Module]/None/NotRecommended: A[#Module#]; name=A
// IMPORT-DAG: Decl[Module]/None/NotRecommended: B[#Module#]; name=B
// IMPORT-DAG: Decl[Module]/None: C[#Module#]; name=C

View File

@@ -172,3 +172,49 @@ func testCompleteErrorTypeInCatch() {
// CATHC2-DAG: Decl[EnumElement]/CurrNominal/TypeRelation[Convertible]: E2({#Int32#})[#Error4#]; name=E2(Int32)
// CATCH2: End completions
}
func testCompleteInStringLiteral() {
struct Island {
var turnipPrice: String
}
func takeTrailingClosure(_ x: () -> Void) -> Text { fatalError() }
struct BStack<Content> {
init(@ViewBuilder content: () -> Content) {}
}
protocol View {}
struct Text: View {
init(_ x: String) {}
var body: Never { fatalError() }
}
@resultBuilder struct ViewBuilder {
static func buildBlock() -> Text { fatalError() }
static func buildBlock<C: View>(_ c: C) -> C { return c }
static func buildBlock<C1: View, C2: View>(_ c: C1, _ d: C2) -> C1 { return c }
}
func foo(island: Island) {
BStack {
let b = "\(island.#^STRING_LITERAL_VAR^#turnipPrice)"
takeTrailingClosure {}
}
// STRING_LITERAL_VAR: Begin completions, 2 items
// STRING_LITERAL_VAR-DAG: Keyword[self]/CurrNominal: self[#Island#]; name=self
// STRING_LITERAL_VAR-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: turnipPrice[#String#]; name=turnipPrice
// STRING_LITERAL_VAR: End completions
func bar(island: Island) {
BStack {
Text("\(island.#^STRING_LITERAL_AS_ARGUMENT?check=STRING_LITERAL_VAR^#turnipPrice)")
takeTrailingClosure {}
}
}
}

View File

@@ -21,7 +21,7 @@ if #available(OSX 10.51, *) && #available(OSX 10.52, *) { // expected-error {{ex
}
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 }}
if #available { // expected-error {{expected availability condition}}
}
if #available( { // expected-error {{expected platform name}} expected-error {{expected ')'}} expected-note {{to match this opening '('}}

View File

@@ -19,7 +19,7 @@ if #unavailable(OSX 10.51) && #unavailable(OSX 10.52) { // expected-error {{expe
}
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 }}
if #unavailable { // expected-error {{expected availability condition}}
}
if #unavailable( { // expected-error {{expected platform name}} expected-error {{expected ')'}} expected-note {{to match this opening '('}}
@@ -124,4 +124,4 @@ if #available(macOS 10, *) {
if #available(*) == false { // expected-error {{#available cannot be used as an expression, did you mean to use '#unavailable'?}} {{4-14=#unavailable}} {{18-27=}}
}
if !#available(*) { // expected-error {{#available cannot be used as an expression, did you mean to use '#unavailable'?}} {{4-15=#unavailable}}
}
}

View File

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

View File

@@ -7,7 +7,7 @@ func ~= (x: (Int,Int), y: (Int,Int)) -> Bool {
}
func parseError1(x: Int) {
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 }}
switch func {} // expected-error {{expected expression in 'switch' statement}} expected-error {{expected identifier in function declaration}}
}
func parseError2(x: Int) {

View File

@@ -108,8 +108,8 @@ func produce(fn: () -> Int?, default d: () -> Int) -> Int { // expected-note {{d
return fn() ?? d()
}
// TODO: The diagnostics here are perhaps a little overboard.
_ = 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?}}
_ = produce { 2 } `default`: { 3 }
_ = 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}}
_ = produce { 2 } `default`: { 3 } // expected-error {{labeled block needs 'do'}} expected-warning {{integer literal is unused}}
func f() -> Int { 42 }

View File

@@ -14,9 +14,6 @@ if false {
class { // expected-error {{unknown declaration syntax exists in the source}}
// expected-error@-1 {{expected identifier in class declaration}}
// expected-note@-2 {{did you mean to use a 'do' statement?}}
// expected-error@-3 {{closure expression is unused}}
// expected-error@-4 {{top-level statement cannot begin with a closure expression}}
}

View File

@@ -82,7 +82,7 @@ prefix func +// this should be a comment, not an operator
prefix func -/* this also should be a comment, not an operator */
(arg: Int) -> Int { return arg }
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 }}
func +*/ () {} // expected-error {{expected identifier in function declaration}} expected-error {{unexpected end of block comment}}
func errors() {
*/ // expected-error {{unexpected end of block comment}}

View File

@@ -13,8 +13,8 @@ static override func gf5() {} // expected-error {{static methods may only be dec
class override func gf6() {} // expected-error {{class methods may only be declared on a type}}{{1-7=}}
// expected-error@-1 {{'override' can only be specified on class members}}{{7-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 }}
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 }}
static gf7() {} // expected-error {{expected declaration}}
class gf8() {} // expected-error {{expected '{' in class}}
func inGlobalFunc() {
static func gf1() {} // expected-error {{static methods may only be declared on a type}}{{3-10=}}

View File

@@ -136,7 +136,7 @@ if 1 != 2, 4 == 57, let x = opt {} // expected-warning {{immutable value 'x' was
// Test that these don't cause the parser to crash.
if true { if a == 0; {} } // expected-error {{cannot find 'a' in scope}} expected-error {{expected '{' after 'if' condition}}
if a == 0, where b == 0 {} // expected-error 4{{}} expected-note {{}} {{25-25=do }}
if a == 0, where b == 0 {} // expected-error {{cannot find 'a' in scope}} expected-error {{expected expression in conditional}}