Skip to content

Commit 49bfcd2

Browse files
committed
Replace improper 'async' in parser
By replacing the 'async' with 'await' in the parser, we avoid the issue of cascading errors as the compiler gets more and more confused by what it's reading. Instead, everything mostly passes and we just emit the one error message. One caveat that I hadn't taken into account before was that we could have a function called "async", in which case we don't want to replace the "async" keyword with "await".
1 parent 0445f4f commit 49bfcd2

File tree

4 files changed

+28
-28
lines changed

4 files changed

+28
-28
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,6 @@ ERROR(snake_case_deprecated,none,
958958
"%0 has been replaced with %1 in Swift 3",
959959
(StringRef, StringRef))
960960

961-
962961
// Assignment statement
963962
ERROR(expected_expr_assignment,none,
964963
"expected expression in assignment", ())
@@ -986,6 +985,10 @@ NOTE(indent_expression_to_silence,none,
986985
ERROR(expected_expr_throw,PointsToFirstBadToken,
987986
"expected expression in 'throw' statement", ())
988987

988+
// Await/Async
989+
ERROR(expected_await_not_async,none,
990+
"found 'async' in expression; did you mean 'await'?", ())
991+
989992
// Yield Statment
990993
ERROR(expected_expr_yield,PointsToFirstBadToken,
991994
"expected expression in 'yield' statement", ())

lib/Parse/ParseExpr.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,17 @@ ParserResult<Expr> Parser::parseExprSequenceElement(Diag<> message,
399399
SyntaxParsingContext ElementContext(SyntaxContext,
400400
SyntaxContextKind::Expr);
401401

402-
if (Tok.isContextualKeyword("await")) {
403-
if (shouldParseExperimentalConcurrency()) {
402+
if (shouldParseExperimentalConcurrency()) {
403+
// A function called "async" is possible, so we don't want to replace it
404+
// with await.
405+
bool isReplaceableAsync = Tok.isContextualKeyword("async") &&
406+
!peekToken().is(tok::l_paren);
407+
if (Tok.isContextualKeyword("await") || isReplaceableAsync) {
408+
// Error on a replaceable async
409+
if (isReplaceableAsync) {
410+
diagnose(Tok.getLoc(), diag::expected_await_not_async)
411+
.fixItReplace(Tok.getLoc(), "await");
412+
}
404413
SourceLoc awaitLoc = consumeToken();
405414
ParserResult<Expr> sub =
406415
parseExprSequenceElement(diag::expected_expr_after_await, isExprBasic);
@@ -416,12 +425,12 @@ ParserResult<Expr> Parser::parseExprSequenceElement(Diag<> message,
416425
sub = makeParserResult(new (Context) AwaitExpr(awaitLoc, sub.get()));
417426
}
418427

419-
return sub;
420-
} else {
421-
// warn that future versions of Swift will parse this token differently.
422-
diagnose(Tok.getLoc(), diag::warn_await_keyword)
423-
.fixItReplace(Tok.getLoc(), "`await`");
428+
return sub;
424429
}
430+
} else if (Tok.isContextualKeyword("await")) {
431+
// warn that future versions of Swift will parse this token differently.
432+
diagnose(Tok.getLoc(), diag::warn_await_keyword)
433+
.fixItReplace(Tok.getLoc(), "`await`");
425434
}
426435

427436
SourceLoc tryLoc;

lib/Sema/PreCheckExpr.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -519,15 +519,7 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
519519
diag.highlight(UDRE->getSourceRange());
520520
typo->addFixits(diag);
521521
} else {
522-
if (Name.isSimpleName("async")) {
523-
auto diag = Context.Diags.diagnose(Loc,
524-
diag::cannot_find_in_scope_corrected, Name,
525-
/*isOperator=*/false, "await");
526-
diag.highlight(UDRE->getSourceRange());
527-
diag.fixItReplace(Loc, "await");
528-
} else {
529-
emitBasicError();
530-
}
522+
emitBasicError();
531523
}
532524

533525
corrections.noteAllCandidates();

test/Concurrency/await_typo_correction.swift

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,21 @@ func anotherAsyncFunc() async -> Int {
77
return 42
88
}
99

10+
func async() throws { }
11+
1012
@main struct MyProgram {
1113
static func main() async throws {
12-
// expected-error@+7 {{cannot find 'async' in scope; did you mean 'await'?}}{{9-14=await}}
13-
// expected-error@+6 {{consecutive statements on a line must be separated by ';'}}
14-
// expected-error@+5 {{call is 'async' but is not marked with 'await'}}
15-
// expected-error@+4 {{call can throw but is not marked with 'try'}}
16-
// expected-note@+3 {{did you mean to use 'try'?}}
17-
// expected-note@+2 {{did you mean to handle error as optional value?}}
18-
// expected-note@+1 {{did you mean to disable error propagation?}}
14+
// expected-error@+1 {{found 'async' in expression; did you mean 'await'?}}{{9-14=await}}
1915
try async asyncFunc()
2016

21-
// expected-error@+4 {{consecutive statements on a line must be separated by ';'}}
22-
// expected-error@+3 {{cannot find 'async' in scope; did you mean 'await'?}}{{13-18=await}}
23-
// expected-warning@+2 {{result of call to 'anotherAsyncFunc()' is unused}}
24-
// expected-error@+1 {{call is 'async' but is not marked with 'await'}}
17+
// expected-error@+1 {{found 'async' in expression; did you mean 'await'?}}{{13-18=await}}
2518
let _ = async anotherAsyncFunc()
2619

2720
// Don't emit a diagnostic here
2821
async let foo = anotherAsyncFunc()
2922
let _ = await foo
23+
24+
// I question the name choice, but it's valid
25+
try async()
3026
}
3127
}

0 commit comments

Comments
 (0)