[AST Walker] Stop visiting the bodies of closures as expressions.

Single-expression closures have always been traversed differently
from multi-statement closures. The former were traversed as if the
expression was their only child, skipping the BraceStmt and implicit
return, while the later was traversed as a normal BraceStmt.
Unify on the latter treatment, so that traversal

There are a few places where we unintentionally relied on this
expression-as-child behavior. Clean those up to work with arbitrary
closures, which is an overall simplification in the logic.
This commit is contained in:
Doug Gregor
2020-06-04 23:06:32 -07:00
parent ad42d477f7
commit 836bc57fe5
13 changed files with 102 additions and 63 deletions

View File

@@ -1615,27 +1615,9 @@ private:
bool walkToExprPre(Expr *E) override {
auto SR = E->getSourceRange();
if (SR.isValid() && SM.rangeContainsTokenLoc(SR, TargetLoc)) {
if (auto closure = dyn_cast<ClosureExpr>(E)) {
if (closure->hasSingleExpressionBody()) {
// Treat a single-expression body like a brace statement and reset
// the enclosing context. Note: when the placeholder is the whole
// body it is handled specially as wrapped in braces by
// shouldUseTrailingClosureInTuple().
auto SR = closure->getSingleExpressionBody()->getSourceRange();
if (SR.isValid() && SR.Start != TargetLoc &&
SM.rangeContainsTokenLoc(SR, TargetLoc)) {
OuterStmt = nullptr;
OuterExpr = nullptr;
EnclosingCallAndArg = {nullptr, nullptr};
return true;
}
}
}
if (!checkCallExpr(E) && !EnclosingCallAndArg.first) {
OuterExpr = E;
}
if (SR.isValid() && SM.rangeContainsTokenLoc(SR, TargetLoc) &&
!checkCallExpr(E) && !EnclosingCallAndArg.first) {
OuterExpr = E;
}
return true;
}
@@ -1646,11 +1628,30 @@ private:
return true;
}
/// Whether this statement body consists of only an implicit "return",
/// possibly within braces.
bool isImplicitReturnBody(Stmt *S) {
if (auto RS = dyn_cast<ReturnStmt>(S))
return RS->isImplicit() && RS->getSourceRange().Start == TargetLoc;
if (auto BS = dyn_cast<BraceStmt>(S)) {
if (BS->getNumElements() == 1) {
if (auto innerS = BS->getFirstElement().dyn_cast<Stmt *>())
return isImplicitReturnBody(innerS);
}
}
return false;
}
bool walkToStmtPre(Stmt *S) override {
auto SR = S->getSourceRange();
if (SR.isValid() && SM.rangeContainsTokenLoc(SR, TargetLoc)) {
if (SR.isValid() && SM.rangeContainsTokenLoc(SR, TargetLoc) &&
!isImplicitReturnBody(S)) {
// A statement inside an expression - e.g. `foo({ if ... })` - resets
// the enclosing context.
//
// ... unless it's an implicit return.
OuterExpr = nullptr;
EnclosingCallAndArg = {nullptr, nullptr};
@@ -1766,7 +1767,6 @@ public:
ArrayRef<ClosureInfo> trailingClosures)>
MultiClosureCallback,
std::function<bool(EditorPlaceholderExpr *)> NonClosureCallback) {
SourceLoc PlaceholderStartLoc = SM.getLocForOffset(BufID, Offset);
// See if the placeholder is encapsulated with an EditorPlaceholderExpr