Skip to content

Commit 5b2f40a

Browse files
authored
Merge pull request #37411 from hamishknight/returning-so-soon
2 parents 2074057 + 87703a0 commit 5b2f40a

File tree

4 files changed

+95
-16
lines changed

4 files changed

+95
-16
lines changed

include/swift/IDE/SourceEntityWalker.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SWIFT_IDE_SOURCE_ENTITY_WALKER_H
1515

1616
#include "swift/AST/ASTWalker.h"
17+
#include "swift/Basic/Defer.h"
1718
#include "swift/Basic/LLVM.h"
1819
#include "swift/Basic/SourceLoc.h"
1920
#include "llvm/ADT/PointerUnion.h"
@@ -176,6 +177,24 @@ class SourceEntityWalker {
176177
virtual ~SourceEntityWalker() {}
177178

178179
virtual void anchor();
180+
181+
/// Retrieve the current ASTWalker being used to traverse the AST.
182+
const ASTWalker &getWalker() const {
183+
assert(Walker && "Not walking!");
184+
return *Walker;
185+
}
186+
187+
private:
188+
ASTWalker *Walker = nullptr;
189+
190+
/// Utility that lets us keep track of an ASTWalker when walking.
191+
bool performWalk(ASTWalker &W, llvm::function_ref<bool(void)> DoWalk) {
192+
Walker = &W;
193+
SWIFT_DEFER {
194+
Walker = nullptr;
195+
};
196+
return DoWalk();
197+
}
179198
};
180199

181200
} // namespace swift

lib/IDE/Refactoring.cpp

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4623,16 +4623,24 @@ class NodesToPrint {
46234623
!Nodes.back().isImplicit();
46244624
}
46254625

4626-
/// If the last recorded node is an explicit return or break statement, drop
4627-
/// it from the list.
4628-
void dropTrailingReturnOrBreak() {
4626+
/// If the last recorded node is an explicit return or break statement that
4627+
/// can be safely dropped, drop it from the list.
4628+
void dropTrailingReturnOrBreakIfPossible() {
46294629
if (!hasTrailingReturnOrBreak())
46304630
return;
46314631

4632+
auto *Node = Nodes.back().get<Stmt *>();
4633+
4634+
// If this is a return statement with return expression, let's preserve it.
4635+
if (auto *RS = dyn_cast<ReturnStmt>(Node)) {
4636+
if (RS->hasResult())
4637+
return;
4638+
}
4639+
46324640
// Remove the node from the list, but make sure to add it as a possible
46334641
// comment loc to preserve any of its attached comments.
4634-
auto Node = Nodes.pop_back_val();
4635-
addPossibleCommentLoc(Node.getStartLoc());
4642+
Nodes.pop_back();
4643+
addPossibleCommentLoc(Node->getStartLoc());
46364644
}
46374645

46384646
/// Returns a list of nodes to print in a brace statement. This picks up the
@@ -4895,7 +4903,7 @@ struct CallbackClassifier {
48954903
NodesToPrint Nodes) {
48964904
if (Nodes.hasTrailingReturnOrBreak()) {
48974905
CurrentBlock = OtherBlock;
4898-
Nodes.dropTrailingReturnOrBreak();
4906+
Nodes.dropTrailingReturnOrBreakIfPossible();
48994907
Block->addAllNodes(std::move(Nodes));
49004908
} else {
49014909
Block->addAllNodes(std::move(Nodes));
@@ -5600,12 +5608,14 @@ class AsyncConverter : private SourceEntityWalker {
56005608
// it would have been lifted out of the switch statement.
56015609
if (auto *SS = dyn_cast<SwitchStmt>(BS->getTarget())) {
56025610
if (HandledSwitches.contains(SS))
5603-
replaceRangeWithPlaceholder(S->getSourceRange());
5611+
return replaceRangeWithPlaceholder(S->getSourceRange());
56045612
}
56055613
} else if (isa<ReturnStmt>(S) && NestedExprCount == 0) {
56065614
// For a return, if it's not nested inside another closure or function,
56075615
// turn it into a placeholder, as it will be lifted out of the callback.
5608-
replaceRangeWithPlaceholder(S->getSourceRange());
5616+
// Note that we only turn the 'return' token into a placeholder as we
5617+
// still want to be able to apply transforms to the argument.
5618+
replaceRangeWithPlaceholder(S->getStartLoc());
56095619
}
56105620
}
56115621
return true;
@@ -5734,15 +5744,23 @@ class AsyncConverter : private SourceEntityWalker {
57345744
void addHandlerCall(const CallExpr *CE) {
57355745
auto Exprs = TopHandler.extractResultArgs(CE);
57365746

5747+
bool AddedReturnOrThrow = true;
57375748
if (!Exprs.isError()) {
5738-
OS << tok::kw_return;
5749+
// It's possible the user has already written an explicit return statement
5750+
// for the completion handler call, e.g 'return completion(args...)'. In
5751+
// that case, be sure not to add another return.
5752+
auto *parent = getWalker().Parent.getAsStmt();
5753+
AddedReturnOrThrow = !(parent && isa<ReturnStmt>(parent));
5754+
if (AddedReturnOrThrow)
5755+
OS << tok::kw_return;
57395756
} else {
57405757
OS << tok::kw_throw;
57415758
}
57425759

57435760
ArrayRef<Expr *> Args = Exprs.args();
57445761
if (!Args.empty()) {
5745-
OS << " ";
5762+
if (AddedReturnOrThrow)
5763+
OS << " ";
57465764
if (Args.size() > 1)
57475765
OS << tok::l_paren;
57485766
for (size_t I = 0, E = Args.size(); I < E; ++I) {

lib/IDE/SourceEntityWalker.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -806,32 +806,32 @@ bool SemaAnnotator::shouldIgnore(Decl *D) {
806806

807807
bool SourceEntityWalker::walk(SourceFile &SrcFile) {
808808
SemaAnnotator Annotator(*this);
809-
return SrcFile.walk(Annotator);
809+
return performWalk(Annotator, [&]() { return SrcFile.walk(Annotator); });
810810
}
811811

812812
bool SourceEntityWalker::walk(ModuleDecl &Mod) {
813813
SemaAnnotator Annotator(*this);
814-
return Mod.walk(Annotator);
814+
return performWalk(Annotator, [&]() { return Mod.walk(Annotator); });
815815
}
816816

817817
bool SourceEntityWalker::walk(Stmt *S) {
818818
SemaAnnotator Annotator(*this);
819-
return S->walk(Annotator);
819+
return performWalk(Annotator, [&]() { return S->walk(Annotator); });
820820
}
821821

822822
bool SourceEntityWalker::walk(Expr *E) {
823823
SemaAnnotator Annotator(*this);
824-
return E->walk(Annotator);
824+
return performWalk(Annotator, [&]() { return E->walk(Annotator); });
825825
}
826826

827827
bool SourceEntityWalker::walk(Decl *D) {
828828
SemaAnnotator Annotator(*this);
829-
return D->walk(Annotator);
829+
return performWalk(Annotator, [&]() { return D->walk(Annotator); });
830830
}
831831

832832
bool SourceEntityWalker::walk(DeclContext *DC) {
833833
SemaAnnotator Annotator(*this);
834-
return DC->walkContext(Annotator);
834+
return performWalk(Annotator, [&]() { return DC->walkContext(Annotator); });
835835
}
836836

837837
bool SourceEntityWalker::walk(ASTNode N) {

test/refactoring/ConvertAsync/convert_function.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,45 @@ func voidResultCompletion(completion: (Result<Void, Error>) -> Void) {
247247
// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NON-COMPLETION-HANDLER %s
248248
func functionWithSomeHandler(handler: (String) -> Void) {}
249249
// NON-COMPLETION-HANDLER: func functionWithSomeHandler() async -> String {}
250+
251+
// rdar://77789360 Make sure we don't print a double return statement.
252+
// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RETURN-HANDLING %s
253+
func testReturnHandling(_ completion: (String?, Error?) -> Void) {
254+
return completion("", nil)
255+
}
256+
// RETURN-HANDLING: func testReturnHandling() async throws -> String {
257+
// RETURN-HANDLING-NEXT: {{^}} return ""{{$}}
258+
// RETURN-HANDLING-NEXT: }
259+
260+
// rdar://77789360 Make sure we don't print a double return statement and don't
261+
// completely drop completion(a).
262+
// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RETURN-HANDLING2 %s
263+
func testReturnHandling2(completion: @escaping (String) -> ()) {
264+
testReturnHandling { x, err in
265+
guard let x = x else {
266+
let a = ""
267+
return completion(a)
268+
}
269+
let b = ""
270+
return completion(b)
271+
}
272+
}
273+
// RETURN-HANDLING2: func testReturnHandling2() async -> String {
274+
// RETURN-HANDLING2-NEXT: do {
275+
// RETURN-HANDLING2-NEXT: let x = try await testReturnHandling()
276+
// RETURN-HANDLING2-NEXT: let b = ""
277+
// RETURN-HANDLING2-NEXT: {{^}}<#return#> b{{$}}
278+
// RETURN-HANDLING2-NEXT: } catch let err {
279+
// RETURN-HANDLING2-NEXT: let a = ""
280+
// RETURN-HANDLING2-NEXT: {{^}}<#return#> a{{$}}
281+
// RETURN-HANDLING2-NEXT: }
282+
// RETURN-HANDLING2-NEXT: }
283+
284+
// FIXME: We should arguably be able to handle transforming this completion handler call (rdar://78011350).
285+
// RUN: %refactor -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=RETURN-HANDLING3 %s
286+
func testReturnHandling3(_ completion: (String?, Error?) -> Void) {
287+
return (completion("", nil))
288+
}
289+
// RETURN-HANDLING3: func testReturnHandling3() async throws -> String {
290+
// RETURN-HANDLING3-NEXT: {{^}} return (<#completion#>("", nil)){{$}}
291+
// RETURN-HANDLING3-NEXT: }

0 commit comments

Comments
 (0)