Skip to content

Commit d8780d3

Browse files
committed
[Parse] give more useful errors for forget 'do' keyword.
1 parent f6f756e commit d8780d3

File tree

5 files changed

+38
-22
lines changed

5 files changed

+38
-22
lines changed

include/swift/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,8 @@ class Parser {
963963
/// \returns \c true if there was a parsing error.
964964
bool parseTopLevelSIL();
965965

966+
bool isStartOfGetSetAccessor();
967+
966968
/// Flags that control the parsing of declarations.
967969
enum ParseDeclFlags {
968970
PD_Default = 0,

lib/Parse/ParseDecl.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6683,13 +6683,19 @@ Parser::parseDeclVar(ParseDeclOptions Flags,
66836683
// If we syntactically match the second decl-var production, with a
66846684
// var-get-set clause, parse the var-get-set clause.
66856685
if (Tok.is(tok::l_brace)) {
6686-
HasAccessors = true;
6687-
auto boundVar =
6688-
parseDeclVarGetSet(PBDEntries.back(),
6689-
Flags, StaticLoc, StaticSpelling, VarLoc,
6690-
PatternInit != nullptr, Attributes, Decls);
6691-
if (boundVar.hasCodeCompletion())
6692-
return makeResult(makeParserCodeCompletionStatus());
6686+
6687+
// Skip parsing the var-get-set clause if '{' is at start of line
6688+
// and next token is not 'didSet' or 'willSet'. Parsing as 'do'
6689+
// statement gives useful errors for missing 'do' before brace.
6690+
// See SR-14836.
6691+
if (!PatternInit || !Tok.isAtStartOfLine() || isStartOfGetSetAccessor()) {
6692+
HasAccessors = true;
6693+
auto boundVar = parseDeclVarGetSet(
6694+
PBDEntries.back(), Flags, StaticLoc, StaticSpelling, VarLoc,
6695+
PatternInit != nullptr, Attributes, Decls);
6696+
if (boundVar.hasCodeCompletion())
6697+
return makeResult(makeParserCodeCompletionStatus());
6698+
}
66936699
}
66946700

66956701
// Propagate back types for simple patterns, like "var A, B : T".

lib/Parse/ParseExpr.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -941,9 +941,9 @@ StringRef Parser::copyAndStripUnderscores(StringRef orig) {
941941
/// possibly preceded by attributes. If so, we disambiguate the parse as the
942942
/// start of a get-set block in a variable definition (not as a trailing
943943
/// closure).
944-
static bool isStartOfGetSetAccessor(Parser &P) {
945-
assert(P.Tok.is(tok::l_brace) && "not checking a brace?");
946-
944+
bool Parser::isStartOfGetSetAccessor() {
945+
assert(Tok.is(tok::l_brace) && "not checking a brace?");
946+
947947
// The only case this can happen is if the accessor label is immediately after
948948
// a brace (possibly preceded by attributes). "get" is implicit, so it can't
949949
// be checked for. Conveniently however, get/set properties are not allowed
@@ -952,7 +952,7 @@ static bool isStartOfGetSetAccessor(Parser &P) {
952952
//
953953
// If we have a 'didSet' or a 'willSet' label, disambiguate immediately as
954954
// an accessor block.
955-
Token NextToken = P.peekToken();
955+
Token NextToken = peekToken();
956956
if (NextToken.isContextualKeyword("didSet") ||
957957
NextToken.isContextualKeyword("willSet"))
958958
return true;
@@ -961,21 +961,23 @@ static bool isStartOfGetSetAccessor(Parser &P) {
961961
if (NextToken.isNot(tok::at_sign))
962962
return false;
963963

964-
Parser::BacktrackingScope Backtrack(P);
964+
Parser::BacktrackingScope Backtrack(*this);
965965

966966
// Eat the "{".
967-
P.consumeToken(tok::l_brace);
967+
consumeToken(tok::l_brace);
968968

969969
// Eat attributes, if present.
970-
while (P.consumeIf(tok::at_sign)) {
971-
if (!P.consumeIf(tok::identifier)) return false;
970+
while (consumeIf(tok::at_sign)) {
971+
if (!consumeIf(tok::identifier))
972+
return false;
972973
// Eat paren after attribute name; e.g. @foo(x)
973-
if (P.Tok.is(tok::l_paren)) P.skipSingle();
974+
if (Tok.is(tok::l_paren))
975+
skipSingle();
974976
}
975977

976978
// Check if we have 'didSet'/'willSet' after attributes.
977-
return P.Tok.isContextualKeyword("didSet") ||
978-
P.Tok.isContextualKeyword("willSet");
979+
return Tok.isContextualKeyword("didSet") ||
980+
Tok.isContextualKeyword("willSet");
979981
}
980982

981983
/// Recover invalid uses of trailing closures in a situation
@@ -987,7 +989,7 @@ static bool isValidTrailingClosure(bool isExprBasic, Parser &P){
987989

988990
// If this is the start of a get/set accessor, then it isn't a trailing
989991
// closure.
990-
if (isStartOfGetSetAccessor(P))
992+
if (P.isStartOfGetSetAccessor())
991993
return false;
992994

993995
// If this is a normal expression (not an expr-basic) then trailing closures

test/Constraints/closures.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ func test13811882() {
8989
// <rdar://problem/21544303> QoI: "Unexpected trailing closure" should have a fixit to insert a 'do' statement
9090
// <https://bugs.swift.org/browse/SR-3671>
9191
func r21544303() {
92-
var inSubcall = true
93-
{
94-
} // expected-error {{computed property must have accessors specified}}
92+
var inSubcall = true
93+
{ // expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}}
94+
}
9595
inSubcall = false
9696

9797
// This is a problem, but isn't clear what was intended.

test/expr/closure/let.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@ func foo() {
1111
_ = { x = 0 }() // expected-error{{'x' is a 'let'}}
1212
_ = { frob(x: x); x = 0 }() // expected-error 2 {{'x' is a 'let'}}
1313
}
14+
15+
let a: Int
16+
{ 1 } // expected-error{{'let' declarations cannot be computed properties}}
17+
18+
let b: Int = 1
19+
{ didSet { print("didSet") } } // expected-error{{'let' declarations cannot be observing properties}}

0 commit comments

Comments
 (0)