[analyzer] Treat C++ 'throw' as a sink.

Our current handling of 'throw' is all CFG-based: it jumps to a 'catch' block
if there is one and the function exit block if not. But this doesn't really
get the right behavior when a function is inlined: execution will continue on
the caller's side, which is always the wrong thing to do.

Even within a single function, 'throw' completely skips any destructors that
are to be run. This is essentially the same problem as @finally -- a CFGBlock
that can have multiple entry points, whose exit points depend on whether it
was entered normally or exceptionally.

Representing 'throw' as a sink matches our current (non-)handling of @throw.
It's not a perfect solution, but it's better than continuing analysis in an
inconsistent or even impossible state.

<rdar://problem/12113713>

llvm-svn: 162157
This commit is contained in:
Jordan Rose 2012-08-18 00:30:23 +00:00
parent a97a99736e
commit a4309c941c
3 changed files with 21 additions and 9 deletions

View File

@ -607,11 +607,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::AtomicExprClass:
// Fall through.
// Currently all handling of 'throw' just falls to the CFG. We
// can consider doing more if necessary.
case Stmt::CXXThrowExprClass:
// Fall through.
// Cases we intentionally don't evaluate, since they don't need
// to be explicitly evaluated.
case Stmt::AddrLabelExprClass:
@ -886,12 +881,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
case Stmt::ObjCAtThrowStmtClass: {
case Stmt::ObjCAtThrowStmtClass:
case Stmt::CXXThrowExprClass:
// FIXME: This is not complete. We basically treat @throw as
// an abort.
Bldr.generateNode(S, Pred, Pred->getState(), /*IsSink=*/true);
break;
}
case Stmt::ReturnStmtClass:
Bldr.takeNodes(Pred);

View File

@ -21,3 +21,18 @@ int testObjC() {
return a; // no-warning
}
void inlinedCXX() {
clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
throw -1;
}
int testCXX() {
int a; // uninitialized
// FIXME: this should be reported as a leak, because C++ exceptions are
// often not fatal.
void *mem = malloc(4);
inlinedCXX();
free(mem);
return a; // no-warning
}

View File

@ -537,7 +537,8 @@ MyEnum rdar10892489_positive() {
throw MyEnumValue;
} catch (MyEnum e) {
int *p = 0;
*p = 0xDEADBEEF; // expected-warning {{null}}
// FALSE NEGATIVE
*p = 0xDEADBEEF; // {{null}}
return e;
}
return MyEnumValue;
@ -562,7 +563,8 @@ void PR11545_positive() {
catch (...)
{
int *p = 0;
*p = 0xDEADBEEF; // expected-warning {{null}}
// FALSE NEGATIVE
*p = 0xDEADBEEF; // {{null}}
}
}