Skip to content

Commit ffe038a

Browse files
authored
Merge pull request #37419 from hamishknight/returning-so-soon-5.5
2 parents 4f97332 + f84ec6a commit ffe038a

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
@@ -4660,16 +4660,24 @@ class NodesToPrint {
46604660
!Nodes.back().isImplicit();
46614661
}
46624662

4663-
/// If the last recorded node is an explicit return or break statement, drop
4664-
/// it from the list.
4665-
void dropTrailingReturnOrBreak() {
4663+
/// If the last recorded node is an explicit return or break statement that
4664+
/// can be safely dropped, drop it from the list.
4665+
void dropTrailingReturnOrBreakIfPossible() {
46664666
if (!hasTrailingReturnOrBreak())
46674667
return;
46684668

4669+
auto *Node = Nodes.back().get<Stmt *>();
4670+
4671+
// If this is a return statement with return expression, let's preserve it.
4672+
if (auto *RS = dyn_cast<ReturnStmt>(Node)) {
4673+
if (RS->hasResult())
4674+
return;
4675+
}
4676+
46694677
// Remove the node from the list, but make sure to add it as a possible
46704678
// comment loc to preserve any of its attached comments.
4671-
auto Node = Nodes.pop_back_val();
4672-
addPossibleCommentLoc(Node.getStartLoc());
4679+
Nodes.pop_back();
4680+
addPossibleCommentLoc(Node->getStartLoc());
46734681
}
46744682

46754683
/// Returns a list of nodes to print in a brace statement. This picks up the
@@ -4932,7 +4940,7 @@ struct CallbackClassifier {
49324940
NodesToPrint Nodes) {
49334941
if (Nodes.hasTrailingReturnOrBreak()) {
49344942
CurrentBlock = OtherBlock;
4935-
Nodes.dropTrailingReturnOrBreak();
4943+
Nodes.dropTrailingReturnOrBreakIfPossible();
49364944
Block->addAllNodes(std::move(Nodes));
49374945
} else {
49384946
Block->addAllNodes(std::move(Nodes));
@@ -5641,12 +5649,14 @@ class AsyncConverter : private SourceEntityWalker {
56415649
// it would have been lifted out of the switch statement.
56425650
if (auto *SS = dyn_cast<SwitchStmt>(BS->getTarget())) {
56435651
if (HandledSwitches.contains(SS))
5644-
replaceRangeWithPlaceholder(S->getSourceRange());
5652+
return replaceRangeWithPlaceholder(S->getSourceRange());
56455653
}
56465654
} else if (isa<ReturnStmt>(S) && NestedExprCount == 0) {
56475655
// For a return, if it's not nested inside another closure or function,
56485656
// turn it into a placeholder, as it will be lifted out of the callback.
5649-
replaceRangeWithPlaceholder(S->getSourceRange());
5657+
// Note that we only turn the 'return' token into a placeholder as we
5658+
// still want to be able to apply transforms to the argument.
5659+
replaceRangeWithPlaceholder(S->getStartLoc());
56505660
}
56515661
}
56525662
return true;
@@ -5775,15 +5785,23 @@ class AsyncConverter : private SourceEntityWalker {
57755785
void addHandlerCall(const CallExpr *CE) {
57765786
auto Exprs = TopHandler.extractResultArgs(CE);
57775787

5788+
bool AddedReturnOrThrow = true;
57785789
if (!Exprs.isError()) {
5779-
OS << tok::kw_return;
5790+
// It's possible the user has already written an explicit return statement
5791+
// for the completion handler call, e.g 'return completion(args...)'. In
5792+
// that case, be sure not to add another return.
5793+
auto *parent = getWalker().Parent.getAsStmt();
5794+
AddedReturnOrThrow = !(parent && isa<ReturnStmt>(parent));
5795+
if (AddedReturnOrThrow)
5796+
OS << tok::kw_return;
57805797
} else {
57815798
OS << tok::kw_throw;
57825799
}
57835800

57845801
ArrayRef<Expr *> Args = Exprs.args();
57855802
if (!Args.empty()) {
5786-
OS << " ";
5803+
if (AddedReturnOrThrow)
5804+
OS << " ";
57875805
if (Args.size() > 1)
57885806
OS << tok::l_paren;
57895807
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)