mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 '('}}
|
||||
|
||||
@@ -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}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<}> {}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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}}
|
||||
|
||||
|
||||
@@ -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=}}
|
||||
|
||||
@@ -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}}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user