mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user