зеркало из https://github.com/microsoft/clang-1.git
Add EvalCallExpr interface to checker, and migrate the no-return function
handler to this interface. GRExprEngine::CheckerEvalCall() will return true if one of the checkers has processed the node. In the future this might return void when we have some default checker. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90755 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
863dbcb45e
Коммит
935ef90f4b
|
@ -174,6 +174,14 @@ private:
|
|||
return EvalNilReceiver(C, ME);
|
||||
}
|
||||
|
||||
bool GR_EvalCallExpr(ExplodedNodeSet &Dst, GRStmtNodeBuilder &Builder,
|
||||
GRExprEngine &Eng, const CallExpr *CE,
|
||||
ExplodedNode *Pred, void *tag) {
|
||||
CheckerContext C(Dst, Builder, Eng, Pred, tag, ProgramPoint::PostStmtKind,
|
||||
CE);
|
||||
return EvalCallExpr(C, CE);
|
||||
}
|
||||
|
||||
// FIXME: Remove the 'tag' option.
|
||||
void GR_VisitBind(ExplodedNodeSet &Dst,
|
||||
GRStmtNodeBuilder &Builder, GRExprEngine &Eng,
|
||||
|
@ -229,6 +237,10 @@ public:
|
|||
virtual bool EvalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} // end clang namespace
|
||||
|
||||
|
|
|
@ -212,6 +212,10 @@ protected:
|
|||
void CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
|
||||
bool isPrevisit);
|
||||
|
||||
bool CheckerEvalCall(const CallExpr *CE,
|
||||
ExplodedNodeSet &Dst,
|
||||
ExplodedNode *Pred);
|
||||
|
||||
void CheckerEvalNilReceiver(const ObjCMessageExpr *ME,
|
||||
ExplodedNodeSet &Dst,
|
||||
const GRState *state,
|
||||
|
|
|
@ -36,6 +36,7 @@ add_clang_library(clangAnalysis
|
|||
MallocChecker.cpp
|
||||
ManagerRegistry.cpp
|
||||
MemRegion.cpp
|
||||
NoReturnFunctionChecker.cpp
|
||||
NSAutoreleasePoolChecker.cpp
|
||||
NSErrorChecker.cpp
|
||||
PathDiagnostic.cpp
|
||||
|
|
|
@ -158,6 +158,27 @@ void GRExprEngine::CheckerEvalNilReceiver(const ObjCMessageExpr *ME,
|
|||
}
|
||||
}
|
||||
|
||||
// CheckerEvalCall returns true if one of the checkers processed the node.
|
||||
// This may return void when all call evaluation logic goes to some checker
|
||||
// in the future.
|
||||
bool GRExprEngine::CheckerEvalCall(const CallExpr *CE,
|
||||
ExplodedNodeSet &Dst,
|
||||
ExplodedNode *Pred) {
|
||||
bool Evaluated = false;
|
||||
|
||||
for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) {
|
||||
void *tag = I->first;
|
||||
Checker *checker = I->second;
|
||||
|
||||
if (checker->GR_EvalCallExpr(Dst, *Builder, *this, CE, Pred, tag)) {
|
||||
Evaluated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Evaluated;
|
||||
}
|
||||
|
||||
// FIXME: This is largely copy-paste from CheckerVisit(). Need to
|
||||
// unify.
|
||||
void GRExprEngine::CheckerVisitBind(const Stmt *AssignE, const Stmt *StoreE,
|
||||
|
@ -220,6 +241,9 @@ static void RegisterInternalChecks(GRExprEngine &Eng) {
|
|||
RegisterUndefinedAssignmentChecker(Eng);
|
||||
RegisterUndefBranchChecker(Eng);
|
||||
RegisterUndefResultChecker(Eng);
|
||||
|
||||
// This is not a checker yet.
|
||||
RegisterNoReturnFunctionChecker(Eng);
|
||||
}
|
||||
|
||||
GRExprEngine::GRExprEngine(AnalysisManager &mgr)
|
||||
|
@ -1516,45 +1540,6 @@ static bool EvalOSAtomic(ExplodedNodeSet& Dst,
|
|||
//===----------------------------------------------------------------------===//
|
||||
// Transfer function: Function calls.
|
||||
//===----------------------------------------------------------------------===//
|
||||
static void MarkNoReturnFunction(const FunctionDecl *FD, CallExpr *CE,
|
||||
const GRState *state,
|
||||
GRStmtNodeBuilder *Builder) {
|
||||
if (!FD)
|
||||
return;
|
||||
|
||||
if (FD->getAttr<NoReturnAttr>() ||
|
||||
FD->getAttr<AnalyzerNoReturnAttr>())
|
||||
Builder->BuildSinks = true;
|
||||
else {
|
||||
// HACK: Some functions are not marked noreturn, and don't return.
|
||||
// Here are a few hardwired ones. If this takes too long, we can
|
||||
// potentially cache these results.
|
||||
using llvm::StringRef;
|
||||
bool BuildSinks
|
||||
= llvm::StringSwitch<bool>(StringRef(FD->getIdentifier()->getName()))
|
||||
.Case("exit", true)
|
||||
.Case("panic", true)
|
||||
.Case("error", true)
|
||||
.Case("Assert", true)
|
||||
// FIXME: This is just a wrapper around throwing an exception.
|
||||
// Eventually inter-procedural analysis should handle this easily.
|
||||
.Case("ziperr", true)
|
||||
.Case("assfail", true)
|
||||
.Case("db_error", true)
|
||||
.Case("__assert", true)
|
||||
.Case("__assert_rtn", true)
|
||||
.Case("__assert_fail", true)
|
||||
.Case("dtrace_assfail", true)
|
||||
.Case("yy_fatal_error", true)
|
||||
.Case("_XCAssertionFailureHandler", true)
|
||||
.Case("_DTAssertionFailureHandler", true)
|
||||
.Case("_TSAssertionFailureHandler", true)
|
||||
.Default(false);
|
||||
|
||||
if (BuildSinks)
|
||||
Builder->BuildSinks = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool GRExprEngine::EvalBuiltinFunction(const FunctionDecl *FD, CallExpr *CE,
|
||||
ExplodedNode *Pred,
|
||||
|
@ -1666,32 +1651,41 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred,
|
|||
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
|
||||
const FunctionDecl* FD = L.getAsFunctionDecl();
|
||||
|
||||
MarkNoReturnFunction(FD, CE, state, Builder);
|
||||
ExplodedNodeSet DstTmp3, DstChecker, DstOther;
|
||||
|
||||
// Evaluate the call.
|
||||
if (EvalBuiltinFunction(FD, CE, *DI, Dst))
|
||||
continue;
|
||||
// If the callee is processed by a checker, skip the rest logic.
|
||||
if (CheckerEvalCall(CE, DstChecker, *DI))
|
||||
DstTmp3 = DstChecker;
|
||||
else {
|
||||
//MarkNoReturnFunction(FD, CE, state, Builder);
|
||||
|
||||
// Dispatch to the plug-in transfer function.
|
||||
SaveOr OldHasGen(Builder->HasGeneratedNode);
|
||||
Pred = *DI;
|
||||
// Evaluate the call.
|
||||
if (EvalBuiltinFunction(FD, CE, *DI, Dst))
|
||||
continue;
|
||||
|
||||
// Dispatch to transfer function logic to handle the call itself.
|
||||
// FIXME: Allow us to chain together transfer functions.
|
||||
assert(Builder && "GRStmtNodeBuilder must be defined.");
|
||||
ExplodedNodeSet DstTmp;
|
||||
// Dispatch to the plug-in transfer function.
|
||||
SaveOr OldHasGen(Builder->HasGeneratedNode);
|
||||
Pred = *DI;
|
||||
|
||||
// Dispatch to transfer function logic to handle the call itself.
|
||||
// FIXME: Allow us to chain together transfer functions.
|
||||
assert(Builder && "GRStmtNodeBuilder must be defined.");
|
||||
|
||||
if (!EvalOSAtomic(DstTmp, *this, *Builder, CE, L, Pred))
|
||||
getTF().EvalCall(DstTmp, *this, *Builder, CE, L, Pred);
|
||||
|
||||
// Handle the case where no nodes where generated. Auto-generate that
|
||||
// contains the updated state if we aren't generating sinks.
|
||||
if (!Builder->BuildSinks && DstTmp.empty() &&
|
||||
!Builder->HasGeneratedNode)
|
||||
MakeNode(DstTmp, CE, Pred, state);
|
||||
|
||||
if (!EvalOSAtomic(DstOther, *this, *Builder, CE, L, Pred))
|
||||
getTF().EvalCall(DstOther, *this, *Builder, CE, L, Pred);
|
||||
|
||||
// Handle the case where no nodes where generated. Auto-generate that
|
||||
// contains the updated state if we aren't generating sinks.
|
||||
if (!Builder->BuildSinks && DstTmp3.empty() &&
|
||||
!Builder->HasGeneratedNode)
|
||||
MakeNode(DstOther, CE, Pred, state);
|
||||
|
||||
DstTmp3 = DstOther;
|
||||
}
|
||||
|
||||
// Perform the post-condition check of the CallExpr.
|
||||
CheckerVisit(CE, Dst, DstTmp, false);
|
||||
CheckerVisit(CE, Dst, DstTmp3, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,5 +37,7 @@ void RegisterUndefinedAssignmentChecker(GRExprEngine &Eng);
|
|||
void RegisterUndefBranchChecker(GRExprEngine &Eng);
|
||||
void RegisterUndefResultChecker(GRExprEngine &Eng);
|
||||
|
||||
void RegisterNoReturnFunctionChecker(GRExprEngine &Eng);
|
||||
|
||||
} // end clang namespace
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This defines NoReturnFunctionChecker, which evaluates functions that do not
|
||||
// return to the caller.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "GRExprEngineInternalChecks.h"
|
||||
#include "clang/Analysis/PathSensitive/Checker.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
|
||||
class NoReturnFunctionChecker : public Checker {
|
||||
public:
|
||||
static void *getTag() { static int tag = 0; return &tag; }
|
||||
virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void clang::RegisterNoReturnFunctionChecker(GRExprEngine &Eng) {
|
||||
Eng.registerCheck(new NoReturnFunctionChecker());
|
||||
}
|
||||
|
||||
bool NoReturnFunctionChecker::EvalCallExpr(CheckerContext &C,
|
||||
const CallExpr *CE) {
|
||||
const GRState *state = C.getState();
|
||||
const Expr *Callee = CE->getCallee();
|
||||
SVal L = state->getSVal(Callee);
|
||||
const FunctionDecl *FD = L.getAsFunctionDecl();
|
||||
if (!FD)
|
||||
return false;
|
||||
|
||||
bool BuildSinks = false;
|
||||
|
||||
if (FD->getAttr<NoReturnAttr>() || FD->getAttr<AnalyzerNoReturnAttr>())
|
||||
BuildSinks = true;
|
||||
else {
|
||||
// HACK: Some functions are not marked noreturn, and don't return.
|
||||
// Here are a few hardwired ones. If this takes too long, we can
|
||||
// potentially cache these results.
|
||||
using llvm::StringRef;
|
||||
BuildSinks
|
||||
= llvm::StringSwitch<bool>(StringRef(FD->getIdentifier()->getName()))
|
||||
.Case("exit", true)
|
||||
.Case("panic", true)
|
||||
.Case("error", true)
|
||||
.Case("Assert", true)
|
||||
// FIXME: This is just a wrapper around throwing an exception.
|
||||
// Eventually inter-procedural analysis should handle this easily.
|
||||
.Case("ziperr", true)
|
||||
.Case("assfail", true)
|
||||
.Case("db_error", true)
|
||||
.Case("__assert", true)
|
||||
.Case("__assert_rtn", true)
|
||||
.Case("__assert_fail", true)
|
||||
.Case("dtrace_assfail", true)
|
||||
.Case("yy_fatal_error", true)
|
||||
.Case("_XCAssertionFailureHandler", true)
|
||||
.Case("_DTAssertionFailureHandler", true)
|
||||
.Case("_TSAssertionFailureHandler", true)
|
||||
.Default(false);
|
||||
}
|
||||
|
||||
if (!BuildSinks)
|
||||
return false;
|
||||
|
||||
C.GenerateSink(CE);
|
||||
return true;
|
||||
}
|
Загрузка…
Ссылка в новой задаче