@@ -427,6 +427,7 @@ class EffectsHandlingWalker : public ASTWalker {
427427 }
428428
429429 std::pair<bool , Expr*> walkToExprPre (Expr *E) override {
430+ visitExprPre (E);
430431 ShouldRecurse_t recurse = ShouldRecurse;
431432 if (isa<ErrorExpr>(E)) {
432433 asImpl ().flagInvalidCode ();
@@ -486,6 +487,8 @@ class EffectsHandlingWalker : public ASTWalker {
486487 ShouldRecurse_t checkForEach (ForEachStmt *S) {
487488 return ShouldRecurse;
488489 }
490+
491+ void visitExprPre (Expr *expr) { asImpl ().visitExprPre (expr); }
489492};
490493
491494// / A potential reason why something might have an effect.
@@ -1098,6 +1101,8 @@ class ApplyClassifier {
10981101 // guaranteed to give back None, which leaves our ThrowKind unchanged.
10991102 }
11001103 }
1104+
1105+ void visitExprPre (Expr *expr) { return ; }
11011106 };
11021107
11031108 class FunctionAsyncClassifier
@@ -1182,6 +1187,8 @@ class ApplyClassifier {
11821187
11831188 return ShouldRecurse;
11841189 }
1190+
1191+ void visitExprPre (Expr *expr) { return ; }
11851192 };
11861193
11871194 Optional<ConditionalEffectKind>
@@ -1429,6 +1436,11 @@ class Context {
14291436 DiagnoseErrorOnTry = b;
14301437 }
14311438
1439+ // / Return true when the current context is under an interpolated string
1440+ bool isWithinInterpolatedString () const {
1441+ return InterpolatedString != nullptr ;
1442+ }
1443+
14321444 // / Stores the location of the innermost await
14331445 SourceLoc awaitLoc = SourceLoc();
14341446
@@ -1900,67 +1912,6 @@ class Context {
19001912 }
19011913 }
19021914
1903- void diagnoseUncoveredAsyncSite (ASTContext &ctx, ASTNode node,
1904- PotentialEffectReason reason) {
1905- SourceRange highlight = node.getSourceRange ();
1906- auto diag = diag::async_call_without_await;
1907-
1908- switch (reason.getKind ()) {
1909- case PotentialEffectReason::Kind::AsyncLet:
1910- // Reference to an 'async let' missing an 'await'.
1911- if (auto declR = dyn_cast_or_null<DeclRefExpr>(node.dyn_cast <Expr*>())) {
1912- if (auto var = dyn_cast<VarDecl>(declR->getDecl ())) {
1913- if (var->isAsyncLet ()) {
1914- ctx.Diags .diagnose (declR->getLoc (), diag::async_let_without_await,
1915- var->getName ());
1916- return ;
1917- }
1918- }
1919- }
1920- LLVM_FALLTHROUGH; // fallthrough to a message about property access
1921-
1922- case PotentialEffectReason::Kind::PropertyAccess:
1923- diag = diag::async_prop_access_without_await;
1924- break ;
1925-
1926- case PotentialEffectReason::Kind::SubscriptAccess:
1927- diag = diag::async_subscript_access_without_await;
1928- break ;
1929-
1930- case PotentialEffectReason::Kind::ByClosure:
1931- case PotentialEffectReason::Kind::ByDefaultClosure:
1932- case PotentialEffectReason::Kind::ByConformance:
1933- case PotentialEffectReason::Kind::Apply: {
1934- if (Function) {
1935- // To produce a better error message, check if it is an autoclosure.
1936- // We do not use 'Context::isAutoClosure' b/c it gives conservative
1937- // answers.
1938- if (auto autoclosure = dyn_cast_or_null<AutoClosureExpr>(
1939- Function->getAbstractClosureExpr ())) {
1940- switch (autoclosure->getThunkKind ()) {
1941- case AutoClosureExpr::Kind::None:
1942- diag = diag::async_call_without_await_in_autoclosure;
1943- break ;
1944-
1945- case AutoClosureExpr::Kind::AsyncLet:
1946- diag = diag::async_call_without_await_in_async_let;
1947- break ;
1948-
1949- case AutoClosureExpr::Kind::SingleCurryThunk:
1950- case AutoClosureExpr::Kind::DoubleCurryThunk:
1951- break ;
1952- }
1953- }
1954- }
1955- break ;
1956- }
1957- };
1958-
1959- ctx.Diags .diagnose (node.getStartLoc (), diag)
1960- .fixItInsert (node.getStartLoc (), " await " )
1961- .highlight (highlight);
1962- }
1963-
19641915 void diagnoseAsyncInIllegalContext (DiagnosticEngine &Diags, ASTNode node) {
19651916 if (auto *e = node.dyn_cast <Expr*>()) {
19661917 if (isa<ApplyExpr>(e)) {
@@ -2112,6 +2063,66 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
21122063 // / context.
21132064 ConditionalEffectKind MaxThrowingKind;
21142065
2066+ struct DiagnosticInfo {
2067+ DiagnosticInfo (Expr &failingExpr,
2068+ PotentialEffectReason reason) :
2069+ reason (reason),
2070+ expr (failingExpr) {}
2071+
2072+ // / Reason for throwing
2073+ PotentialEffectReason reason;
2074+
2075+ // / Failing expression
2076+ Expr &expr;
2077+ };
2078+
2079+ SmallVector<Expr *, 4 > errorOrder;
2080+ llvm::DenseMap<Expr *, std::vector<DiagnosticInfo>> uncoveredAsync;
2081+ llvm::DenseMap<Expr *, Expr *> parentMap;
2082+
2083+ static bool isEffectAnchor (Expr *e) {
2084+ return isa<AbstractClosureExpr>(e) || isa<DiscardAssignmentExpr>(e) || isa<AssignExpr>(e);
2085+ }
2086+
2087+ static bool isAnchorTooEarly (Expr *e) {
2088+ return isa<AssignExpr>(e) || isa<DiscardAssignmentExpr>(e);
2089+ }
2090+
2091+ // / Find the top location where we should put the await
2092+ static Expr *walkToAnchor (Expr *e, llvm::DenseMap<Expr *, Expr *> &parentMap,
2093+ bool isInterpolatedString) {
2094+ Expr *parent = e;
2095+ Expr *lastParent = e;
2096+ while (parent && !isEffectAnchor (parent)) {
2097+ lastParent = parent;
2098+ parent = parentMap[parent];
2099+ }
2100+
2101+ if (parent && !isAnchorTooEarly (parent)) {
2102+ return parent;
2103+ }
2104+
2105+ if (isInterpolatedString) {
2106+ // TODO: I'm being gentle with the casts to avoid breaking things
2107+ // If we see incorrect fix-it locations in string interpolations
2108+ // we need to change how this behaves
2109+ // Assert builds will crash giving us a bug to fix, non-asserts will
2110+ // quietly "just work".
2111+ assert (parent == nullptr && " Expected to be at top of expression" );
2112+ assert (isa<CallExpr>(lastParent) &&
2113+ " Expected top of string interpolation to be CalExpr" );
2114+ assert (isa<ParenExpr>(dyn_cast<CallExpr>(lastParent)->getArg ()) &&
2115+ " Expected paren expr in string interpolation call" );
2116+ if (CallExpr *callExpr = dyn_cast<CallExpr>(lastParent)) {
2117+ if (ParenExpr *body = dyn_cast<ParenExpr>(callExpr->getArg ())) {
2118+ return body->getSubExpr ();
2119+ }
2120+ }
2121+ }
2122+
2123+ return lastParent;
2124+ }
2125+
21152126 void flagInvalidCode () {
21162127 // Suppress warnings about useless try or catch.
21172128 Flags.set (ContextFlags::HasAnyThrowSite);
@@ -2273,6 +2284,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
22732284 }
22742285 }
22752286
2287+ ~CheckEffectsCoverage () {
2288+ for (Expr *anchor: errorOrder) {
2289+ diagnoseUncoveredAsyncSite (anchor);
2290+ }
2291+ }
2292+
2293+
22762294 // / Mark that the current context is top-level code with
22772295 // / throw-without-try enabled.
22782296 void setTopLevelThrowWithoutTry () {
@@ -2290,6 +2308,12 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
22902308 }
22912309
22922310private:
2311+ void visitExprPre (Expr *expr) {
2312+ if (parentMap.count (expr) == 0 )
2313+ parentMap = expr->getParentMap ();
2314+ return ;
2315+ }
2316+
22932317 ShouldRecurse_t checkClosure (ClosureExpr *E) {
22942318 ContextScope scope (*this , Context::forClosure (E));
22952319 scope.enterSubFunction ();
@@ -2623,8 +2647,18 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
26232647 }
26242648 // Diagnose async calls that are outside of an await context.
26252649 else if (!Flags.has (ContextFlags::IsAsyncCovered)) {
2626- CurContext.diagnoseUncoveredAsyncSite (Ctx, E,
2627- classification.getAsyncReason ());
2650+ Expr *expr = E.dyn_cast <Expr*>();
2651+ Expr *anchor = walkToAnchor (expr, parentMap,
2652+ CurContext.isWithinInterpolatedString ());
2653+
2654+ auto key = uncoveredAsync.find (anchor);
2655+ if (key == uncoveredAsync.end ()) {
2656+ uncoveredAsync.insert ({anchor, {}});
2657+ errorOrder.push_back (anchor);
2658+ }
2659+ uncoveredAsync[anchor].emplace_back (
2660+ *expr,
2661+ classification.getAsyncReason ());
26282662 }
26292663 }
26302664
@@ -2773,6 +2807,78 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
27732807
27742808 return ShouldRecurse;
27752809 }
2810+
2811+ void diagnoseUncoveredAsyncSite (const Expr *anchor) const {
2812+ auto asyncPointIter = uncoveredAsync.find (anchor);
2813+ if (asyncPointIter == uncoveredAsync.end ())
2814+ return ;
2815+ const std::vector<DiagnosticInfo> &errors = asyncPointIter->getSecond ();
2816+ SourceLoc awaitInsertLoc = anchor->getStartLoc ();
2817+ if (const AnyTryExpr *tryExpr = dyn_cast<AnyTryExpr>(anchor))
2818+ awaitInsertLoc = tryExpr->getSubExpr ()->getStartLoc ();
2819+ else if (const AutoClosureExpr *autoClosure = dyn_cast<AutoClosureExpr>(anchor)) {
2820+ if (const AnyTryExpr *tryExpr = dyn_cast<AnyTryExpr>(autoClosure->getSingleExpressionBody ()))
2821+ awaitInsertLoc = tryExpr->getSubExpr ()->getStartLoc ();
2822+ }
2823+
2824+ Ctx.Diags .diagnose (anchor->getStartLoc (), diag::async_expr_without_await)
2825+ .fixItInsert (awaitInsertLoc, " await " )
2826+ .highlight (anchor->getSourceRange ());
2827+
2828+ for (const DiagnosticInfo &diag: errors) {
2829+ switch (diag.reason .getKind ()) {
2830+ case PotentialEffectReason::Kind::AsyncLet:
2831+ if (auto declR = dyn_cast<DeclRefExpr>(&diag.expr )) {
2832+ if (auto var = dyn_cast<VarDecl>(declR->getDecl ())) {
2833+ if (var->isAsyncLet ()) {
2834+ Ctx.Diags .diagnose (declR->getLoc (),
2835+ diag::async_let_without_await,
2836+ var->getName ());
2837+ continue ;
2838+ }
2839+ }
2840+ }
2841+ LLVM_FALLTHROUGH; // fallthrough to a message about PropertyAccess
2842+ case PotentialEffectReason::Kind::PropertyAccess:
2843+ Ctx.Diags .diagnose (diag.expr .getStartLoc (),
2844+ diag::async_access_without_await, 1 );
2845+ continue ;
2846+
2847+ case PotentialEffectReason::Kind::SubscriptAccess:
2848+ Ctx.Diags .diagnose (diag.expr .getStartLoc (),
2849+ diag::async_access_without_await, 2 );
2850+ continue ;
2851+
2852+ case PotentialEffectReason::Kind::ByClosure:
2853+ case PotentialEffectReason::Kind::ByDefaultClosure:
2854+ case PotentialEffectReason::Kind::ByConformance:
2855+ case PotentialEffectReason::Kind::Apply: {
2856+ if (auto autoclosure = dyn_cast<AutoClosureExpr>(anchor)) {
2857+ switch (autoclosure->getThunkKind ()) {
2858+ case AutoClosureExpr::Kind::None:
2859+ Ctx.Diags .diagnose (diag.expr .getStartLoc (),
2860+ diag::async_call_without_await_in_autoclosure);
2861+ break ;
2862+ case AutoClosureExpr::Kind::AsyncLet:
2863+ Ctx.Diags .diagnose (diag.expr .getStartLoc (),
2864+ diag::async_call_without_await_in_async_let);
2865+ break ;
2866+ case AutoClosureExpr::Kind::SingleCurryThunk:
2867+ case AutoClosureExpr::Kind::DoubleCurryThunk:
2868+ Ctx.Diags .diagnose (diag.expr .getStartLoc (),
2869+ diag::async_access_without_await, 0 );
2870+ break ;
2871+ }
2872+ continue ;
2873+ }
2874+ Ctx.Diags .diagnose (diag.expr .getStartLoc (),
2875+ diag::async_access_without_await, 0 );
2876+
2877+ continue ;
2878+ }
2879+ }
2880+ }
2881+ }
27762882};
27772883
27782884// Find nested functions and perform effects checking on them.
0 commit comments