Skip to content

Commit 703a122

Browse files
authored
Merge pull request #37670 from hamishknight/let-that-async-in-5.5
[5.5] [Refactoring] Support async for function extraction
2 parents 07c1374 + 610fdd7 commit 703a122

File tree

14 files changed

+175
-36
lines changed

14 files changed

+175
-36
lines changed

include/swift/AST/Effects.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#define SWIFT_EFFECTS_H
2727

2828
#include "swift/AST/Type.h"
29+
#include "swift/Basic/OptionSet.h"
2930

3031
#include <utility>
3132

@@ -34,11 +35,14 @@ class raw_ostream;
3435
}
3536

3637
namespace swift {
38+
class AbstractFunctionDecl;
39+
class ProtocolDecl;
3740

3841
enum class EffectKind : uint8_t {
39-
Throws,
40-
Async
42+
Throws = 1 << 0,
43+
Async = 1 << 1
4144
};
45+
using PossibleEffects = OptionSet<EffectKind>;
4246

4347
void simple_display(llvm::raw_ostream &out, const EffectKind kind);
4448

include/swift/IDE/Utils.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/Basic/LLVM.h"
1818
#include "swift/AST/ASTNode.h"
1919
#include "swift/AST/DeclNameLoc.h"
20+
#include "swift/AST/Effects.h"
2021
#include "swift/AST/Module.h"
2122
#include "swift/AST/ASTPrinter.h"
2223
#include "swift/IDE/SourceEntityWalker.h"
@@ -345,7 +346,7 @@ struct ResolvedRangeInfo {
345346
ArrayRef<Token> TokensInRange;
346347
CharSourceRange ContentRange;
347348
bool HasSingleEntry;
348-
bool ThrowingUnhandledError;
349+
PossibleEffects UnhandledEffects;
349350
OrphanKind Orphan;
350351

351352
// The topmost ast nodes contained in the given range.
@@ -359,15 +360,15 @@ struct ResolvedRangeInfo {
359360
ArrayRef<Token> TokensInRange,
360361
DeclContext* RangeContext,
361362
Expr *CommonExprParent, bool HasSingleEntry,
362-
bool ThrowingUnhandledError,
363+
PossibleEffects UnhandledEffects,
363364
OrphanKind Orphan, ArrayRef<ASTNode> ContainedNodes,
364365
ArrayRef<DeclaredDecl> DeclaredDecls,
365366
ArrayRef<ReferencedDecl> ReferencedDecls): Kind(Kind),
366367
ExitInfo(ExitInfo),
367368
TokensInRange(TokensInRange),
368369
ContentRange(calculateContentRange(TokensInRange)),
369370
HasSingleEntry(HasSingleEntry),
370-
ThrowingUnhandledError(ThrowingUnhandledError),
371+
UnhandledEffects(UnhandledEffects),
371372
Orphan(Orphan), ContainedNodes(ContainedNodes),
372373
DeclaredDecls(DeclaredDecls),
373374
ReferencedDecls(ReferencedDecls),
@@ -376,7 +377,7 @@ struct ResolvedRangeInfo {
376377
ResolvedRangeInfo(ArrayRef<Token> TokensInRange) :
377378
ResolvedRangeInfo(RangeKind::Invalid, {nullptr, ExitState::Unsure},
378379
TokensInRange, nullptr, /*Commom Expr Parent*/nullptr,
379-
/*Single entry*/true, /*unhandled error*/false,
380+
/*Single entry*/true, /*UnhandledEffects*/{},
380381
OrphanKind::None, {}, {}, {}) {}
381382
ResolvedRangeInfo(): ResolvedRangeInfo(ArrayRef<Token>()) {}
382383
void print(llvm::raw_ostream &OS) const;

lib/IDE/IDERequests.cpp

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "swift/AST/ASTPrinter.h"
1414
#include "swift/AST/Decl.h"
15+
#include "swift/AST/Effects.h"
1516
#include "swift/AST/NameLookup.h"
1617
#include "swift/AST/ASTDemangler.h"
1718
#include "swift/Basic/SourceManager.h"
@@ -377,41 +378,45 @@ class RangeResolver : public SourceEntityWalker {
377378
ResolvedRangeInfo resolve();
378379
};
379380

380-
static bool hasUnhandledError(ArrayRef<ASTNode> Nodes) {
381-
class ThrowingEntityAnalyzer : public SourceEntityWalker {
382-
bool Throwing;
381+
static PossibleEffects getUnhandledEffects(ArrayRef<ASTNode> Nodes) {
382+
class EffectsAnalyzer : public SourceEntityWalker {
383+
PossibleEffects Effects;
383384
public:
384-
ThrowingEntityAnalyzer(): Throwing(false) {}
385385
bool walkToStmtPre(Stmt *S) override {
386386
if (auto DCS = dyn_cast<DoCatchStmt>(S)) {
387387
if (DCS->isSyntacticallyExhaustive())
388388
return false;
389-
Throwing = true;
389+
Effects |= EffectKind::Throws;
390390
} else if (isa<ThrowStmt>(S)) {
391-
Throwing = true;
391+
Effects |= EffectKind::Throws;
392392
}
393-
return !Throwing;
393+
return true;
394394
}
395395
bool walkToExprPre(Expr *E) override {
396-
if (isa<TryExpr>(E)) {
397-
Throwing = true;
398-
}
399-
return !Throwing;
396+
// Don't walk into closures, they only produce effects when called.
397+
if (isa<ClosureExpr>(E))
398+
return false;
399+
400+
if (isa<TryExpr>(E))
401+
Effects |= EffectKind::Throws;
402+
if (isa<AwaitExpr>(E))
403+
Effects |= EffectKind::Async;
404+
405+
return true;
400406
}
401407
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
402408
return false;
403409
}
404-
bool walkToDeclPost(Decl *D) override { return !Throwing; }
405-
bool walkToStmtPost(Stmt *S) override { return !Throwing; }
406-
bool walkToExprPost(Expr *E) override { return !Throwing; }
407-
bool isThrowing() { return Throwing; }
410+
PossibleEffects getEffects() const { return Effects; }
408411
};
409412

410-
return Nodes.end() != std::find_if(Nodes.begin(), Nodes.end(), [](ASTNode N) {
411-
ThrowingEntityAnalyzer Analyzer;
413+
PossibleEffects Effects;
414+
for (auto N : Nodes) {
415+
EffectsAnalyzer Analyzer;
412416
Analyzer.walk(N);
413-
return Analyzer.isThrowing();
414-
});
417+
Effects |= Analyzer.getEffects();
418+
}
419+
return Effects;
415420
}
416421

417422
struct RangeResolver::Implementation {
@@ -549,7 +554,7 @@ struct RangeResolver::Implementation {
549554
assert(ContainedASTNodes.size() == 1);
550555
// Single node implies single entry point, or is it?
551556
bool SingleEntry = true;
552-
bool UnhandledError = hasUnhandledError({Node});
557+
auto UnhandledEffects = getUnhandledEffects({Node});
553558
OrphanKind Kind = getOrphanKind(ContainedASTNodes);
554559
if (Node.is<Expr*>())
555560
return ResolvedRangeInfo(RangeKind::SingleExpression,
@@ -558,7 +563,7 @@ struct RangeResolver::Implementation {
558563
getImmediateContext(),
559564
/*Common Parent Expr*/nullptr,
560565
SingleEntry,
561-
UnhandledError, Kind,
566+
UnhandledEffects, Kind,
562567
llvm::makeArrayRef(ContainedASTNodes),
563568
llvm::makeArrayRef(DeclaredDecls),
564569
llvm::makeArrayRef(ReferencedDecls));
@@ -569,7 +574,7 @@ struct RangeResolver::Implementation {
569574
getImmediateContext(),
570575
/*Common Parent Expr*/nullptr,
571576
SingleEntry,
572-
UnhandledError, Kind,
577+
UnhandledEffects, Kind,
573578
llvm::makeArrayRef(ContainedASTNodes),
574579
llvm::makeArrayRef(DeclaredDecls),
575580
llvm::makeArrayRef(ReferencedDecls));
@@ -581,7 +586,7 @@ struct RangeResolver::Implementation {
581586
getImmediateContext(),
582587
/*Common Parent Expr*/nullptr,
583588
SingleEntry,
584-
UnhandledError, Kind,
589+
UnhandledEffects, Kind,
585590
llvm::makeArrayRef(ContainedASTNodes),
586591
llvm::makeArrayRef(DeclaredDecls),
587592
llvm::makeArrayRef(ReferencedDecls));
@@ -642,7 +647,7 @@ struct RangeResolver::Implementation {
642647
getImmediateContext(),
643648
Parent,
644649
hasSingleEntryPoint(ContainedASTNodes),
645-
hasUnhandledError(ContainedASTNodes),
650+
getUnhandledEffects(ContainedASTNodes),
646651
getOrphanKind(ContainedASTNodes),
647652
llvm::makeArrayRef(ContainedASTNodes),
648653
llvm::makeArrayRef(DeclaredDecls),
@@ -889,7 +894,7 @@ struct RangeResolver::Implementation {
889894
TokensInRange,
890895
getImmediateContext(), nullptr,
891896
hasSingleEntryPoint(ContainedASTNodes),
892-
hasUnhandledError(ContainedASTNodes),
897+
getUnhandledEffects(ContainedASTNodes),
893898
getOrphanKind(ContainedASTNodes),
894899
llvm::makeArrayRef(ContainedASTNodes),
895900
llvm::makeArrayRef(DeclaredDecls),
@@ -904,7 +909,7 @@ struct RangeResolver::Implementation {
904909
getImmediateContext(),
905910
/*Common Parent Expr*/ nullptr,
906911
/*SinleEntry*/ true,
907-
hasUnhandledError(ContainedASTNodes),
912+
getUnhandledEffects(ContainedASTNodes),
908913
getOrphanKind(ContainedASTNodes),
909914
llvm::makeArrayRef(ContainedASTNodes),
910915
llvm::makeArrayRef(DeclaredDecls),

lib/IDE/Refactoring.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1304,7 +1304,9 @@ bool RefactoringActionExtractFunction::performChange() {
13041304
}
13051305
OS << ")";
13061306

1307-
if (RangeInfo.ThrowingUnhandledError)
1307+
if (RangeInfo.UnhandledEffects.contains(EffectKind::Async))
1308+
OS << " async";
1309+
if (RangeInfo.UnhandledEffects.contains(EffectKind::Throws))
13081310
OS << " " << tok::kw_throws;
13091311

13101312
bool InsertedReturnType = false;
@@ -1332,6 +1334,12 @@ bool RefactoringActionExtractFunction::performChange() {
13321334
llvm::raw_svector_ostream OS(Buffer);
13331335
if (RangeInfo.exit() == ExitState::Positive)
13341336
OS << tok::kw_return <<" ";
1337+
1338+
if (RangeInfo.UnhandledEffects.contains(EffectKind::Throws))
1339+
OS << tok::kw_try << " ";
1340+
if (RangeInfo.UnhandledEffects.contains(EffectKind::Async))
1341+
OS << "await ";
1342+
13351343
CallNameOffset = Buffer.size() - ReplaceBegin;
13361344
OS << PreferredName << "(";
13371345
for (auto &RD : Parameters) {

lib/IDE/SwiftSourceDocInfo.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,9 +724,12 @@ void ResolvedRangeInfo::print(llvm::raw_ostream &OS) const {
724724
OS << "<Entry>Multi</Entry>\n";
725725
}
726726

727-
if (ThrowingUnhandledError) {
727+
if (UnhandledEffects.contains(EffectKind::Throws)) {
728728
OS << "<Error>Throwing</Error>\n";
729729
}
730+
if (UnhandledEffects.contains(EffectKind::Async)) {
731+
OS << "<Effect>Async</Effect>\n";
732+
}
730733

731734
if (Orphan != OrphanKind::None) {
732735
OS << "<Orphan>";
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
func longLongLongJourney() async -> Int { 0 }
2+
func longLongLongAwryJourney() async throws -> Int { 0 }
3+
func consumesAsync(_ fn: () async throws -> Void) rethrows {}
4+
5+
fileprivate func new_name() async -> Int {
6+
return await longLongLongJourney()
7+
}
8+
9+
func testThrowingClosure() async throws -> Int {
10+
let x = await new_name()
11+
let y = try await longLongLongAwryJourney() + 1
12+
try consumesAsync { try await longLongLongAwryJourney() }
13+
return x + y
14+
}
15+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
func longLongLongJourney() async -> Int { 0 }
2+
func longLongLongAwryJourney() async throws -> Int { 0 }
3+
func consumesAsync(_ fn: () async throws -> Void) rethrows {}
4+
5+
fileprivate func new_name() async throws -> Int {
6+
return try await longLongLongAwryJourney() + 1
7+
}
8+
9+
func testThrowingClosure() async throws -> Int {
10+
let x = await longLongLongJourney()
11+
let y = try await new_name()
12+
try consumesAsync { try await longLongLongAwryJourney() }
13+
return x + y
14+
}
15+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
func longLongLongJourney() async -> Int { 0 }
2+
func longLongLongAwryJourney() async throws -> Int { 0 }
3+
func consumesAsync(_ fn: () async throws -> Void) rethrows {}
4+
5+
fileprivate func new_name() throws {
6+
try consumesAsync { try await longLongLongAwryJourney() }
7+
}
8+
9+
func testThrowingClosure() async throws -> Int {
10+
let x = await longLongLongJourney()
11+
let y = try await longLongLongAwryJourney() + 1
12+
try new_name()
13+
return x + y
14+
}
15+

test/refactoring/ExtractFunction/Outputs/throw_errors/L13-17.swift.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ func foo2() throws {
1818
do {
1919
try foo1()
2020
} catch {}
21-
new_name()
21+
try new_name()
2222
}
2323

test/refactoring/ExtractFunction/Outputs/throw_errors/L8-8.swift.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ return try foo1()
99
}
1010

1111
func foo2() throws {
12-
new_name()
12+
try new_name()
1313
try! foo1()
1414
do {
1515
try foo1()
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
enum Err : Error {
2+
case wat
3+
}
4+
5+
func throwsSomething() throws { throw Err.wat }
6+
func consumesErrClosure(_ fn: () throws -> Void) {}
7+
func rethrowsErrClosure(_ fn: () throws -> Void) rethrows {}
8+
9+
fileprivate func new_name() {
10+
consumesErrClosure { throw Err.wat }
11+
consumesErrClosure { try throwsSomething() }
12+
}
13+
14+
func testThrowingClosure() throws {
15+
new_name()
16+
try rethrowsErrClosure { try throwsSomething() }
17+
}
18+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
enum Err : Error {
2+
case wat
3+
}
4+
5+
func throwsSomething() throws { throw Err.wat }
6+
func consumesErrClosure(_ fn: () throws -> Void) {}
7+
func rethrowsErrClosure(_ fn: () throws -> Void) rethrows {}
8+
9+
fileprivate func new_name() throws {
10+
consumesErrClosure { throw Err.wat }
11+
consumesErrClosure { try throwsSomething() }
12+
try rethrowsErrClosure { try throwsSomething() }
13+
}
14+
15+
func testThrowingClosure() throws {
16+
try new_name()
17+
}
18+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
func longLongLongJourney() async -> Int { 0 }
2+
func longLongLongAwryJourney() async throws -> Int { 0 }
3+
func consumesAsync(_ fn: () async throws -> Void) rethrows {}
4+
5+
func testThrowingClosure() async throws -> Int {
6+
let x = await longLongLongJourney()
7+
let y = try await longLongLongAwryJourney() + 1
8+
try consumesAsync { try await longLongLongAwryJourney() }
9+
return x + y
10+
}
11+
12+
// RUN: %empty-directory(%t.result)
13+
// RUN: %refactor -extract-function -source-filename %s -pos=6:11 -end-pos=6:38 >> %t.result/async1.swift
14+
// RUN: diff -u %S/Outputs/await/async1.swift.expected %t.result/async1.swift
15+
// RUN: %refactor -extract-function -source-filename %s -pos=7:11 -end-pos=7:50 >> %t.result/async2.swift
16+
// RUN: diff -u %S/Outputs/await/async2.swift.expected %t.result/async2.swift
17+
// RUN: %refactor -extract-function -source-filename %s -pos=8:1 -end-pos=8:60 >> %t.result/consumes_async.swift
18+
// RUN: diff -u %S/Outputs/await/consumes_async.swift.expected %t.result/consumes_async.swift
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
enum Err : Error {
2+
case wat
3+
}
4+
5+
func throwsSomething() throws { throw Err.wat }
6+
func consumesErrClosure(_ fn: () throws -> Void) {}
7+
func rethrowsErrClosure(_ fn: () throws -> Void) rethrows {}
8+
9+
func testThrowingClosure() throws {
10+
consumesErrClosure { throw Err.wat }
11+
consumesErrClosure { try throwsSomething() }
12+
try rethrowsErrClosure { try throwsSomething() }
13+
}
14+
15+
// RUN: %empty-directory(%t.result)
16+
// RUN: %refactor -extract-function -source-filename %s -pos=10:1 -end-pos=11:47 >> %t.result/consumes_err.swift
17+
// RUN: diff -u %S/Outputs/throw_errors3/consumes_err.swift.expected %t.result/consumes_err.swift
18+
// RUN: %refactor -extract-function -source-filename %s -pos=10:1 -end-pos=12:51 >> %t.result/rethrows_err.swift
19+
// RUN: diff -u %S/Outputs/throw_errors3/rethrows_err.swift.expected %t.result/rethrows_err.swift

0 commit comments

Comments
 (0)