[RangeInfo] Add a field in ResolvedRangeInfo to indicate whether the range throws uncatched errors. rdar://30586209 (#7574)

This commit is contained in:
Xi Ge
2017-02-17 17:49:00 -08:00
committed by GitHub
parent 089e12059b
commit 3afb5572c0
3 changed files with 101 additions and 9 deletions

View File

@@ -235,6 +235,7 @@ struct ResolvedRangeInfo {
Type Ty;
StringRef Content;
bool HasSingleEntry;
bool ThrowingUnhandledError;
OrphanKind Orphan;
// The topmost ast nodes contained in the given range.
@@ -244,20 +245,20 @@ struct ResolvedRangeInfo {
DeclContext* RangeContext;
ResolvedRangeInfo(RangeKind Kind, Type Ty, StringRef Content,
DeclContext* RangeContext,
bool HasSingleEntry,
OrphanKind Orphan,
ArrayRef<ASTNode> ContainedNodes,
bool HasSingleEntry, bool ThrowingUnhandledError,
OrphanKind Orphan, ArrayRef<ASTNode> ContainedNodes,
ArrayRef<DeclaredDecl> DeclaredDecls,
ArrayRef<ReferencedDecl> ReferencedDecls): Kind(Kind),
Ty(Ty), Content(Content), HasSingleEntry(HasSingleEntry),
Orphan(Orphan),
ContainedNodes(ContainedNodes),
ThrowingUnhandledError(ThrowingUnhandledError),
Orphan(Orphan), ContainedNodes(ContainedNodes),
DeclaredDecls(DeclaredDecls),
ReferencedDecls(ReferencedDecls),
RangeContext(RangeContext) {}
ResolvedRangeInfo() :
ResolvedRangeInfo(RangeKind::Invalid, Type(), StringRef(), nullptr,
/*Single entry*/true, OrphanKind::None, {}, {}, {}) {}
/*Single entry*/true, /*unhandled error*/false,
OrphanKind::None, {}, {}, {}) {}
void print(llvm::raw_ostream &OS);
};

View File

@@ -217,6 +217,10 @@ void ResolvedRangeInfo::print(llvm::raw_ostream &OS) {
OS << "<Entry>Multi</Entry>\n";
}
if (ThrowingUnhandledError) {
OS << "<Error>Throwing</Error>\n";
}
if (Orphan != OrphanKind::None) {
OS << "<Orphan>";
switch (Orphan) {
@@ -260,6 +264,43 @@ bool ReferencedDecl::operator==(const ReferencedDecl& Other) {
return VD == Other.VD && Ty.getPointer() == Other.Ty.getPointer();
}
static bool hasUnhandledError(ArrayRef<ASTNode> Nodes) {
class ThrowingEntityAnalyzer : public SourceEntityWalker {
bool Throwing;
public:
ThrowingEntityAnalyzer(): Throwing(false) {}
bool walkToStmtPre(Stmt *S) override {
if (auto DCS = dyn_cast<DoCatchStmt>(S)) {
if (DCS->isSyntacticallyExhaustive())
return false;
Throwing = true;
} else if (auto TS = dyn_cast<ThrowStmt>(S)) {
Throwing = true;
}
return !Throwing;
}
bool walkToExprPre(Expr *E) override {
if (auto TE = dyn_cast<TryExpr>(E)) {
Throwing = true;
}
return !Throwing;
}
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
return false;
}
bool walkToDeclPost(Decl *D) override { return !Throwing; }
bool walkToStmtPost(Stmt *S) override { return !Throwing; }
bool walkToExprPost(Expr *E) override { return !Throwing; }
bool isThrowing() { return Throwing; }
};
return Nodes.end() != std::find_if(Nodes.begin(), Nodes.end(), [](ASTNode N) {
ThrowingEntityAnalyzer Analyzer;
N.walk(Analyzer);
return Analyzer.isThrowing();
});
}
struct RangeResolver::Implementation {
SourceFile &File;
ASTContext &Ctx;
@@ -319,24 +360,28 @@ private:
assert(ContainedASTNodes.size() == 1);
// Single node implies single entry point, or is it?
bool SingleEntry = true;
bool UnhandledError = hasUnhandledError({Node});
OrphanKind Kind = getOrphanKind(ContainedASTNodes);
if (Node.is<Expr*>())
return ResolvedRangeInfo(RangeKind::SingleExpression,
resolveNodeType(Node), Content,
getImmediateContext(), SingleEntry, Kind,
getImmediateContext(), SingleEntry,
UnhandledError, Kind,
llvm::makeArrayRef(ContainedASTNodes),
llvm::makeArrayRef(DeclaredDecls),
llvm::makeArrayRef(ReferencedDecls));
else if (Node.is<Stmt*>())
return ResolvedRangeInfo(RangeKind::SingleStatement, resolveNodeType(Node),
Content, getImmediateContext(), SingleEntry, Kind,
Content, getImmediateContext(), SingleEntry,
UnhandledError, Kind,
llvm::makeArrayRef(ContainedASTNodes),
llvm::makeArrayRef(DeclaredDecls),
llvm::makeArrayRef(ReferencedDecls));
else {
assert(Node.is<Decl*>());
return ResolvedRangeInfo(RangeKind::SingleDecl, Type(), Content,
getImmediateContext(), SingleEntry, Kind,
getImmediateContext(), SingleEntry,
UnhandledError, Kind,
llvm::makeArrayRef(ContainedASTNodes),
llvm::makeArrayRef(DeclaredDecls),
llvm::makeArrayRef(ReferencedDecls));
@@ -606,6 +651,7 @@ public:
/* Last node has the type */
resolveNodeType(DCInfo.EndMatches.back()), Content,
getImmediateContext(), hasSingleEntryPoint(ContainedASTNodes),
hasUnhandledError(ContainedASTNodes),
getOrphanKind(ContainedASTNodes),
llvm::makeArrayRef(ContainedASTNodes),
llvm::makeArrayRef(DeclaredDecls),

View File

@@ -0,0 +1,45 @@
func foo() -> Int{
var aaa = 1 + 2
aaa = aaa + 3
if aaa == 3 { aaa = 4 }
return aaa
}
func foo1() throws {}
enum MyError : Error {
case E1
case E2
}
func foo2() throws {
try foo1()
try! foo1()
do {
try foo1()
} catch {}
do {
try foo1()
} catch MyError.E1 {}
do {
throw MyError.E1
} catch MyError.E1 {}
do {
do {
throw MyError.E1
} catch MyError.E1 {}
} catch {}
}
// RUN: %target-swift-ide-test -range -pos=2:1 -end-pos 4:26 -source-filename %s | %FileCheck %s -check-prefix=CHECK-NO-THROW
// RUN: %target-swift-ide-test -range -pos=16:1 -end-pos 16:13 -source-filename %s | %FileCheck %s -check-prefix=CHECK-THROW
// RUN: %target-swift-ide-test -range -pos=17:1 -end-pos 17:14 -source-filename %s | %FileCheck %s -check-prefix=CHECK-NO-THROW
// RUN: %target-swift-ide-test -range -pos=18:1 -end-pos 20:13 -source-filename %s | %FileCheck %s -check-prefix=CHECK-NO-THROW
// RUN: %target-swift-ide-test -range -pos=21:1 -end-pos 23:24 -source-filename %s | %FileCheck %s -check-prefix=CHECK-THROW
// RUN: %target-swift-ide-test -range -pos=24:1 -end-pos 26:24 -source-filename %s | %FileCheck %s -check-prefix=CHECK-THROW
// RUN: %target-swift-ide-test -range -pos=25:1 -end-pos 25:21 -source-filename %s | %FileCheck %s -check-prefix=CHECK-THROW
// RUN: %target-swift-ide-test -range -pos=28:1 -end-pos 30:26 -source-filename %s | %FileCheck %s -check-prefix=CHECK-THROW
// RUN: %target-swift-ide-test -range -pos=27:1 -end-pos 31:13 -source-filename %s | %FileCheck %s -check-prefix=CHECK-NO-THROW
// CHECK-THROW: <Error>Throwing</Error>
// CHECK-NO-THROW-NOT: <Error>Throwing</Error>