Skip to content

Commit 2c4f7b9

Browse files
authored
Merge pull request #35518 from etcwilde/ewilde/did-you-mean-await
[Concurrency] Suggest replacing 'async' with 'await' at call site
2 parents f9be289 + c2be0bd commit 2c4f7b9

File tree

3 files changed

+51
-8
lines changed

3 files changed

+51
-8
lines changed

include/swift/AST/DiagnosticsParse.def

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

963-
964963
// Assignment statement
965964
ERROR(expected_expr_assignment,none,
966965
"expected expression in assignment", ())
@@ -988,6 +987,10 @@ NOTE(indent_expression_to_silence,none,
988987
ERROR(expected_expr_throw,PointsToFirstBadToken,
989988
"expected expression in 'throw' statement", ())
990989

990+
// Await/Async
991+
ERROR(expected_await_not_async,none,
992+
"found 'async' in expression; did you mean 'await'?", ())
993+
991994
// Yield Statment
992995
ERROR(expected_expr_yield,PointsToFirstBadToken,
993996
"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;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency -parse-as-library
2+
// REQUIRES: concurrency
3+
4+
func asyncFunc() async throws {}
5+
6+
func anotherAsyncFunc() async -> Int {
7+
return 42
8+
}
9+
10+
func async() throws { }
11+
12+
@main struct MyProgram {
13+
static func main() async throws {
14+
// expected-error@+1 {{found 'async' in expression; did you mean 'await'?}}{{9-14=await}}
15+
try async asyncFunc()
16+
17+
// expected-error@+2 {{found 'async' in expression; did you mean 'await'?}}{{5-10=await}}
18+
// expected-warning@+1 {{'try' must precede 'await'}}{{5-11=}}{{15-15=await }}
19+
async try asyncFunc()
20+
21+
// expected-error@+1 {{found 'async' in expression; did you mean 'await'?}}{{13-18=await}}
22+
let _ = async anotherAsyncFunc()
23+
24+
// Don't emit a diagnostic here
25+
async let foo = anotherAsyncFunc()
26+
let _ = await foo
27+
28+
// I question the name choice, but it's valid
29+
try async()
30+
}
31+
}

0 commit comments

Comments
 (0)