@@ -427,6 +427,7 @@ class EffectsHandlingWalker : public ASTWalker {
427
427
}
428
428
429
429
std::pair<bool , Expr*> walkToExprPre (Expr *E) override {
430
+ visitExprPre (E);
430
431
ShouldRecurse_t recurse = ShouldRecurse;
431
432
if (isa<ErrorExpr>(E)) {
432
433
asImpl ().flagInvalidCode ();
@@ -486,6 +487,8 @@ class EffectsHandlingWalker : public ASTWalker {
486
487
ShouldRecurse_t checkForEach (ForEachStmt *S) {
487
488
return ShouldRecurse;
488
489
}
490
+
491
+ void visitExprPre (Expr *expr) { asImpl ().visitExprPre (expr); }
489
492
};
490
493
491
494
// / A potential reason why something might have an effect.
@@ -1098,6 +1101,8 @@ class ApplyClassifier {
1098
1101
// guaranteed to give back None, which leaves our ThrowKind unchanged.
1099
1102
}
1100
1103
}
1104
+
1105
+ void visitExprPre (Expr *expr) { return ; }
1101
1106
};
1102
1107
1103
1108
class FunctionAsyncClassifier
@@ -1182,6 +1187,8 @@ class ApplyClassifier {
1182
1187
1183
1188
return ShouldRecurse;
1184
1189
}
1190
+
1191
+ void visitExprPre (Expr *expr) { return ; }
1185
1192
};
1186
1193
1187
1194
Optional<ConditionalEffectKind>
@@ -1429,6 +1436,11 @@ class Context {
1429
1436
DiagnoseErrorOnTry = b;
1430
1437
}
1431
1438
1439
+ // / Return true when the current context is under an interpolated string
1440
+ bool isWithinInterpolatedString () const {
1441
+ return InterpolatedString != nullptr ;
1442
+ }
1443
+
1432
1444
// / Stores the location of the innermost await
1433
1445
SourceLoc awaitLoc = SourceLoc();
1434
1446
@@ -1900,67 +1912,6 @@ class Context {
1900
1912
}
1901
1913
}
1902
1914
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
-
1964
1915
void diagnoseAsyncInIllegalContext (DiagnosticEngine &Diags, ASTNode node) {
1965
1916
if (auto *e = node.dyn_cast <Expr*>()) {
1966
1917
if (isa<ApplyExpr>(e)) {
@@ -2112,6 +2063,66 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
2112
2063
// / context.
2113
2064
ConditionalEffectKind MaxThrowingKind;
2114
2065
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
+
2115
2126
void flagInvalidCode () {
2116
2127
// Suppress warnings about useless try or catch.
2117
2128
Flags.set (ContextFlags::HasAnyThrowSite);
@@ -2273,6 +2284,13 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
2273
2284
}
2274
2285
}
2275
2286
2287
+ ~CheckEffectsCoverage () {
2288
+ for (Expr *anchor: errorOrder) {
2289
+ diagnoseUncoveredAsyncSite (anchor);
2290
+ }
2291
+ }
2292
+
2293
+
2276
2294
// / Mark that the current context is top-level code with
2277
2295
// / throw-without-try enabled.
2278
2296
void setTopLevelThrowWithoutTry () {
@@ -2290,6 +2308,12 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
2290
2308
}
2291
2309
2292
2310
private:
2311
+ void visitExprPre (Expr *expr) {
2312
+ if (parentMap.count (expr) == 0 )
2313
+ parentMap = expr->getParentMap ();
2314
+ return ;
2315
+ }
2316
+
2293
2317
ShouldRecurse_t checkClosure (ClosureExpr *E) {
2294
2318
ContextScope scope (*this , Context::forClosure (E));
2295
2319
scope.enterSubFunction ();
@@ -2623,8 +2647,18 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
2623
2647
}
2624
2648
// Diagnose async calls that are outside of an await context.
2625
2649
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 ());
2628
2662
}
2629
2663
}
2630
2664
@@ -2773,6 +2807,78 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
2773
2807
2774
2808
return ShouldRecurse;
2775
2809
}
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
+ }
2776
2882
};
2777
2883
2778
2884
// Find nested functions and perform effects checking on them.
0 commit comments