Skip to content

Commit 3afb557

Browse files
authored
[RangeInfo] Add a field in ResolvedRangeInfo to indicate whether the range throws uncatched errors. rdar://30586209 (#7574)
1 parent 089e120 commit 3afb557

File tree

3 files changed

+101
-9
lines changed

3 files changed

+101
-9
lines changed

include/swift/IDE/Utils.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ struct ResolvedRangeInfo {
235235
Type Ty;
236236
StringRef Content;
237237
bool HasSingleEntry;
238+
bool ThrowingUnhandledError;
238239
OrphanKind Orphan;
239240

240241
// The topmost ast nodes contained in the given range.
@@ -244,20 +245,20 @@ struct ResolvedRangeInfo {
244245
DeclContext* RangeContext;
245246
ResolvedRangeInfo(RangeKind Kind, Type Ty, StringRef Content,
246247
DeclContext* RangeContext,
247-
bool HasSingleEntry,
248-
OrphanKind Orphan,
249-
ArrayRef<ASTNode> ContainedNodes,
248+
bool HasSingleEntry, bool ThrowingUnhandledError,
249+
OrphanKind Orphan, ArrayRef<ASTNode> ContainedNodes,
250250
ArrayRef<DeclaredDecl> DeclaredDecls,
251251
ArrayRef<ReferencedDecl> ReferencedDecls): Kind(Kind),
252252
Ty(Ty), Content(Content), HasSingleEntry(HasSingleEntry),
253-
Orphan(Orphan),
254-
ContainedNodes(ContainedNodes),
253+
ThrowingUnhandledError(ThrowingUnhandledError),
254+
Orphan(Orphan), ContainedNodes(ContainedNodes),
255255
DeclaredDecls(DeclaredDecls),
256256
ReferencedDecls(ReferencedDecls),
257257
RangeContext(RangeContext) {}
258258
ResolvedRangeInfo() :
259259
ResolvedRangeInfo(RangeKind::Invalid, Type(), StringRef(), nullptr,
260-
/*Single entry*/true, OrphanKind::None, {}, {}, {}) {}
260+
/*Single entry*/true, /*unhandled error*/false,
261+
OrphanKind::None, {}, {}, {}) {}
261262
void print(llvm::raw_ostream &OS);
262263
};
263264

lib/IDE/SwiftSourceDocInfo.cpp

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ void ResolvedRangeInfo::print(llvm::raw_ostream &OS) {
217217
OS << "<Entry>Multi</Entry>\n";
218218
}
219219

220+
if (ThrowingUnhandledError) {
221+
OS << "<Error>Throwing</Error>\n";
222+
}
223+
220224
if (Orphan != OrphanKind::None) {
221225
OS << "<Orphan>";
222226
switch (Orphan) {
@@ -260,6 +264,43 @@ bool ReferencedDecl::operator==(const ReferencedDecl& Other) {
260264
return VD == Other.VD && Ty.getPointer() == Other.Ty.getPointer();
261265
}
262266

267+
static bool hasUnhandledError(ArrayRef<ASTNode> Nodes) {
268+
class ThrowingEntityAnalyzer : public SourceEntityWalker {
269+
bool Throwing;
270+
public:
271+
ThrowingEntityAnalyzer(): Throwing(false) {}
272+
bool walkToStmtPre(Stmt *S) override {
273+
if (auto DCS = dyn_cast<DoCatchStmt>(S)) {
274+
if (DCS->isSyntacticallyExhaustive())
275+
return false;
276+
Throwing = true;
277+
} else if (auto TS = dyn_cast<ThrowStmt>(S)) {
278+
Throwing = true;
279+
}
280+
return !Throwing;
281+
}
282+
bool walkToExprPre(Expr *E) override {
283+
if (auto TE = dyn_cast<TryExpr>(E)) {
284+
Throwing = true;
285+
}
286+
return !Throwing;
287+
}
288+
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
289+
return false;
290+
}
291+
bool walkToDeclPost(Decl *D) override { return !Throwing; }
292+
bool walkToStmtPost(Stmt *S) override { return !Throwing; }
293+
bool walkToExprPost(Expr *E) override { return !Throwing; }
294+
bool isThrowing() { return Throwing; }
295+
};
296+
297+
return Nodes.end() != std::find_if(Nodes.begin(), Nodes.end(), [](ASTNode N) {
298+
ThrowingEntityAnalyzer Analyzer;
299+
N.walk(Analyzer);
300+
return Analyzer.isThrowing();
301+
});
302+
}
303+
263304
struct RangeResolver::Implementation {
264305
SourceFile &File;
265306
ASTContext &Ctx;
@@ -319,24 +360,28 @@ struct RangeResolver::Implementation {
319360
assert(ContainedASTNodes.size() == 1);
320361
// Single node implies single entry point, or is it?
321362
bool SingleEntry = true;
363+
bool UnhandledError = hasUnhandledError({Node});
322364
OrphanKind Kind = getOrphanKind(ContainedASTNodes);
323365
if (Node.is<Expr*>())
324366
return ResolvedRangeInfo(RangeKind::SingleExpression,
325367
resolveNodeType(Node), Content,
326-
getImmediateContext(), SingleEntry, Kind,
368+
getImmediateContext(), SingleEntry,
369+
UnhandledError, Kind,
327370
llvm::makeArrayRef(ContainedASTNodes),
328371
llvm::makeArrayRef(DeclaredDecls),
329372
llvm::makeArrayRef(ReferencedDecls));
330373
else if (Node.is<Stmt*>())
331374
return ResolvedRangeInfo(RangeKind::SingleStatement, resolveNodeType(Node),
332-
Content, getImmediateContext(), SingleEntry, Kind,
375+
Content, getImmediateContext(), SingleEntry,
376+
UnhandledError, Kind,
333377
llvm::makeArrayRef(ContainedASTNodes),
334378
llvm::makeArrayRef(DeclaredDecls),
335379
llvm::makeArrayRef(ReferencedDecls));
336380
else {
337381
assert(Node.is<Decl*>());
338382
return ResolvedRangeInfo(RangeKind::SingleDecl, Type(), Content,
339-
getImmediateContext(), SingleEntry, Kind,
383+
getImmediateContext(), SingleEntry,
384+
UnhandledError, Kind,
340385
llvm::makeArrayRef(ContainedASTNodes),
341386
llvm::makeArrayRef(DeclaredDecls),
342387
llvm::makeArrayRef(ReferencedDecls));
@@ -606,6 +651,7 @@ struct RangeResolver::Implementation {
606651
/* Last node has the type */
607652
resolveNodeType(DCInfo.EndMatches.back()), Content,
608653
getImmediateContext(), hasSingleEntryPoint(ContainedASTNodes),
654+
hasUnhandledError(ContainedASTNodes),
609655
getOrphanKind(ContainedASTNodes),
610656
llvm::makeArrayRef(ContainedASTNodes),
611657
llvm::makeArrayRef(DeclaredDecls),

test/IDE/range_info_throwing.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
func foo() -> Int{
2+
var aaa = 1 + 2
3+
aaa = aaa + 3
4+
if aaa == 3 { aaa = 4 }
5+
return aaa
6+
}
7+
8+
func foo1() throws {}
9+
10+
enum MyError : Error {
11+
case E1
12+
case E2
13+
}
14+
15+
func foo2() throws {
16+
try foo1()
17+
try! foo1()
18+
do {
19+
try foo1()
20+
} catch {}
21+
do {
22+
try foo1()
23+
} catch MyError.E1 {}
24+
do {
25+
throw MyError.E1
26+
} catch MyError.E1 {}
27+
do {
28+
do {
29+
throw MyError.E1
30+
} catch MyError.E1 {}
31+
} catch {}
32+
}
33+
34+
// RUN: %target-swift-ide-test -range -pos=2:1 -end-pos 4:26 -source-filename %s | %FileCheck %s -check-prefix=CHECK-NO-THROW
35+
// RUN: %target-swift-ide-test -range -pos=16:1 -end-pos 16:13 -source-filename %s | %FileCheck %s -check-prefix=CHECK-THROW
36+
// RUN: %target-swift-ide-test -range -pos=17:1 -end-pos 17:14 -source-filename %s | %FileCheck %s -check-prefix=CHECK-NO-THROW
37+
// RUN: %target-swift-ide-test -range -pos=18:1 -end-pos 20:13 -source-filename %s | %FileCheck %s -check-prefix=CHECK-NO-THROW
38+
// RUN: %target-swift-ide-test -range -pos=21:1 -end-pos 23:24 -source-filename %s | %FileCheck %s -check-prefix=CHECK-THROW
39+
// RUN: %target-swift-ide-test -range -pos=24:1 -end-pos 26:24 -source-filename %s | %FileCheck %s -check-prefix=CHECK-THROW
40+
// RUN: %target-swift-ide-test -range -pos=25:1 -end-pos 25:21 -source-filename %s | %FileCheck %s -check-prefix=CHECK-THROW
41+
// RUN: %target-swift-ide-test -range -pos=28:1 -end-pos 30:26 -source-filename %s | %FileCheck %s -check-prefix=CHECK-THROW
42+
// RUN: %target-swift-ide-test -range -pos=27:1 -end-pos 31:13 -source-filename %s | %FileCheck %s -check-prefix=CHECK-NO-THROW
43+
44+
// CHECK-THROW: <Error>Throwing</Error>
45+
// CHECK-NO-THROW-NOT: <Error>Throwing</Error>

0 commit comments

Comments
 (0)