From: Argyrios Kyrtzidis Date: Tue, 22 Feb 2011 17:30:38 +0000 (+0000) Subject: [analyzer] Start moving the path-sensitive checkers to CheckerV2. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=769ce3e93ad35bd9ac28e4d8b8f035ae4fd9a5b5;p=clang [analyzer] Start moving the path-sensitive checkers to CheckerV2. -Migrate ObjCSelfInitChecker to CheckerV2. In the process remove the 'preCallSelfFlags' field from the checker class and use GRState for storing that info. -Get ExprEngine to start delegating checker running to CheckerManager. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@126229 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index 65c8b80aa2..2d81a37d0f 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -16,21 +16,72 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" #include namespace clang { class Decl; + class Stmt; namespace ento { class ExprEngine; class AnalysisManager; class BugReporter; + class CheckerContext; + class ObjCMessage; + class SVal; + class ExplodedNodeSet; + class GRState; + +struct VoidCheckerFnParm {}; +template +class CheckerFn { + typedef void (*Func)(void *, P1, P2, P3, P4); + Func Fn; +public: + void *Checker; + CheckerFn(void *checker, Func fn) : Fn(fn), Checker(checker) { } + void operator()(P1 p1, P2 p2, P3 p3, P4 p4) { Fn(Checker, p1, p2, p3, p4); } +}; + +template +class CheckerFn { + typedef void (*Func)(void *, P1, P2, P3); + Func Fn; +public: + void *Checker; + CheckerFn(void *checker, Func fn) : Fn(fn), Checker(checker) { } + void operator()(P1 p1, P2 p2, P3 p3) { Fn(Checker, p1, p2, p3); } +}; + +template +class CheckerFn { + typedef void (*Func)(void *, P1, P2); + Func Fn; +public: + void *Checker; + CheckerFn(void *checker, Func fn) : Fn(fn), Checker(checker) { } + void operator()(P1 p1, P2 p2) { Fn(Checker, p1, p2); } +}; + +template <> +class CheckerFn { + typedef void (*Func)(void *); + Func Fn; +public: + void *Checker; + CheckerFn(void *checker, Func fn) : Fn(fn), Checker(checker) { } + void operator()() { Fn(Checker); } +}; class CheckerManager { public: ~CheckerManager(); typedef void *CheckerRef; + typedef CheckerFn<> CheckerDtor; //===----------------------------------------------------------------------===// // registerChecker @@ -40,7 +91,7 @@ public: template void registerChecker() { CHECKER *checker = new CHECKER(); - Checkers.push_back(std::pair(checker, destruct)); + CheckerDtors.push_back(CheckerDtor(checker, destruct)); CHECKER::_register(checker, *this); } @@ -50,7 +101,7 @@ public: } //===----------------------------------------------------------------------===// -// Functions for running checkers. +// Functions for running checkers for AST traversing.. //===----------------------------------------------------------------------===// /// \brief Run checkers handling Decls. @@ -62,48 +113,193 @@ public: BugReporter &BR); //===----------------------------------------------------------------------===// -// Internal registration functions. +// Functions for running checkers for path-sensitive checking. +//===----------------------------------------------------------------------===// + + /// \brief Run checkers for pre-visiting Stmts. + void runCheckersForPreStmt(ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + const Stmt *S, + ExprEngine &Eng) { + runCheckersForStmt(/*isPreVisit=*/true, Dst, Src, S, Eng); + } + + /// \brief Run checkers for post-visiting Stmts. + void runCheckersForPostStmt(ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + const Stmt *S, + ExprEngine &Eng) { + runCheckersForStmt(/*isPreVisit=*/false, Dst, Src, S, Eng); + } + + /// \brief Run checkers for visiting Stmts. + void runCheckersForStmt(bool isPreVisit, + ExplodedNodeSet &Dst, ExplodedNodeSet &Src, + const Stmt *S, ExprEngine &Eng); + + /// \brief Run checkers for pre-visiting obj-c messages. + void runCheckersForPreObjCMessage(ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + const ObjCMessage &msg, + ExprEngine &Eng) { + runCheckersForObjCMessage(/*isPreVisit=*/true, Dst, Src, msg, Eng); + } + + /// \brief Run checkers for post-visiting obj-c messages. + void runCheckersForPostObjCMessage(ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + const ObjCMessage &msg, + ExprEngine &Eng) { + runCheckersForObjCMessage(/*isPreVisit=*/false, Dst, Src, msg, Eng); + } + + /// \brief Run checkers for visiting obj-c messages. + void runCheckersForObjCMessage(bool isPreVisit, + ExplodedNodeSet &Dst, ExplodedNodeSet &Src, + const ObjCMessage &msg, ExprEngine &Eng); + + /// \brief Run checkers for load/store of a location. + void runCheckersForLocation(ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + SVal location, bool isLoad, + const Stmt *S, + const GRState *state, + ExprEngine &Eng); + + // FIXME: Temporary until checker running is moved completely into + // CheckerManager. + void registerCheckersToEngine(ExprEngine &eng); + +//===----------------------------------------------------------------------===// +// Internal registration functions for AST traversing. //===----------------------------------------------------------------------===// // Functions used by the registration mechanism, checkers should not touch // these directly. - typedef void (*CheckDeclFunc)(CheckerRef checker, const Decl *D, - AnalysisManager& mgr, BugReporter &BR); + typedef CheckerFn + CheckDeclFunc; + typedef CheckerFn CheckStmtFunc; + typedef bool (*HandlesDeclFunc)(const Decl *D); - void _registerForDecl(CheckerRef checker, CheckDeclFunc checkfn, - HandlesDeclFunc isForDeclFn); + void _registerForDecl(CheckDeclFunc checkfn, HandlesDeclFunc isForDeclFn); - void _registerForBody(CheckerRef checker, CheckDeclFunc checkfn); - - void registerCheckersToEngine(ExprEngine &eng); + void _registerForBody(CheckDeclFunc checkfn); + +//===----------------------------------------------------------------------===// +// Internal registration functions for path-sensitive checking. +//===----------------------------------------------------------------------===// + + typedef CheckerFn CheckObjCMessageFunc; + typedef CheckerFn + CheckLocationFunc; + + typedef bool (*HandlesStmtFunc)(const Stmt *D); + void _registerForPreStmt(CheckStmtFunc checkfn, + HandlesStmtFunc isForStmtFn); + void _registerForPostStmt(CheckStmtFunc checkfn, + HandlesStmtFunc isForStmtFn); + + void _registerForPreObjCMessage(CheckObjCMessageFunc checkfn); + void _registerForPostObjCMessage(CheckObjCMessageFunc checkfn); + + void _registerForLocation(CheckLocationFunc checkfn); + +//===----------------------------------------------------------------------===// +// Implementation details. +//===----------------------------------------------------------------------===// private: template static void destruct(void *obj) { delete static_cast(obj); } + std::vector CheckerDtors; + std::vector Funcs; struct DeclCheckerInfo { - CheckerRef Checker; CheckDeclFunc CheckFn; HandlesDeclFunc IsForDeclFn; }; std::vector DeclCheckers; - std::vector > BodyCheckers; - - typedef void (*Dtor)(void *); - std::vector > Checkers; + std::vector BodyCheckers; - typedef llvm::SmallVector, 4> - CachedDeclCheckers; + typedef llvm::SmallVector CachedDeclCheckers; typedef llvm::DenseMap CachedDeclCheckersMapTy; CachedDeclCheckersMapTy CachedDeclCheckersMap; + + struct StmtCheckerInfo { + CheckStmtFunc CheckFn; + HandlesStmtFunc IsForStmtFn; + bool IsPreVisit; + }; + std::vector StmtCheckers; + + struct CachedStmtCheckersKey { + unsigned StmtKind; + bool IsPreVisit; + + CachedStmtCheckersKey() : StmtKind(0), IsPreVisit(0) { } + CachedStmtCheckersKey(unsigned stmtKind, bool isPreVisit) + : StmtKind(stmtKind), IsPreVisit(isPreVisit) { } + + static CachedStmtCheckersKey getSentinel() { + return CachedStmtCheckersKey(~0U, ~0U); + } + unsigned getHashValue() const { + llvm::FoldingSetNodeID ID; + ID.AddInteger(StmtKind); + ID.AddBoolean(IsPreVisit); + return ID.ComputeHash(); + } + bool operator==(const CachedStmtCheckersKey &RHS) const { + return StmtKind == RHS.StmtKind && IsPreVisit == RHS.IsPreVisit; + } + }; + friend struct llvm::DenseMapInfo; + + typedef llvm::SmallVector CachedStmtCheckers; + typedef llvm::DenseMap + CachedStmtCheckersMapTy; + CachedStmtCheckersMapTy CachedStmtCheckersMap; + + CachedStmtCheckers *getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit); + + std::vector PreObjCMessageCheckers; + std::vector PostObjCMessageCheckers; + + std::vector LocationCheckers; }; } // end ento namespace } // end clang namespace +namespace llvm { + /// Define DenseMapInfo so that CachedStmtCheckersKey can be used as key + /// in DenseMap and DenseSets. + template <> + struct DenseMapInfo { + static inline clang::ento::CheckerManager::CachedStmtCheckersKey + getEmptyKey() { + return clang::ento::CheckerManager::CachedStmtCheckersKey(); + } + static inline clang::ento::CheckerManager::CachedStmtCheckersKey + getTombstoneKey() { + return clang::ento::CheckerManager::CachedStmtCheckersKey::getSentinel(); + } + + static unsigned + getHashValue(clang::ento::CheckerManager::CachedStmtCheckersKey S) { + return S.getHashValue(); + } + + static bool isEqual(clang::ento::CheckerManager::CachedStmtCheckersKey LHS, + clang::ento::CheckerManager::CachedStmtCheckersKey RHS) { + return LHS == RHS; + } + }; +} // end namespace llvm + #endif diff --git a/include/clang/StaticAnalyzer/Core/CheckerV2.h b/include/clang/StaticAnalyzer/Core/CheckerV2.h index 8c96866f1c..1f5b19ca80 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerV2.h +++ b/include/clang/StaticAnalyzer/Core/CheckerV2.h @@ -41,7 +41,9 @@ class ASTDecl { public: template static void _register(CHECKER *checker, CheckerManager &mgr) { - mgr._registerForDecl(checker, _checkDecl, _handlesDecl); + mgr._registerForDecl(CheckerManager::CheckDeclFunc(checker, + _checkDecl), + _handlesDecl); } }; @@ -55,7 +57,91 @@ class ASTCodeBody { public: template static void _register(CHECKER *checker, CheckerManager &mgr) { - mgr._registerForBody(checker, _checkBody); + mgr._registerForBody(CheckerManager::CheckDeclFunc(checker, + _checkBody)); + } +}; + +template +class PreStmt { + template + static void _checkStmt(void *checker, const Stmt *S, CheckerContext &C) { + ((const CHECKER *)checker)->checkPreStmt(llvm::cast(S), C); + } + + static bool _handlesStmt(const Stmt *S) { + return llvm::isa(S); + } +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPreStmt(CheckerManager::CheckStmtFunc(checker, + _checkStmt), + _handlesStmt); + } +}; + +template +class PostStmt { + template + static void _checkStmt(void *checker, const Stmt *S, CheckerContext &C) { + ((const CHECKER *)checker)->checkPostStmt(llvm::cast(S), C); + } + + static bool _handlesStmt(const Stmt *S) { + return llvm::isa(S); + } +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPostStmt(CheckerManager::CheckStmtFunc(checker, + _checkStmt), + _handlesStmt); + } +}; + +class PreObjCMessage { + template + static void _checkObjCMessage(void *checker, const ObjCMessage &msg, + CheckerContext &C) { + ((const CHECKER *)checker)->checkPreObjCMessage(msg, C); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPreObjCMessage( + CheckerManager::CheckObjCMessageFunc(checker, _checkObjCMessage)); + } +}; + +class PostObjCMessage { + template + static void _checkObjCMessage(void *checker, const ObjCMessage &msg, + CheckerContext &C) { + ((const CHECKER *)checker)->checkPostObjCMessage(msg, C); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPostObjCMessage( + CheckerManager::CheckObjCMessageFunc(checker, _checkObjCMessage)); + } +}; + +class Location { + template + static void _checkLocation(void *checker, const SVal &location, bool isLoad, + CheckerContext &C) { + ((const CHECKER *)checker)->checkLocation(location, isLoad, C); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForLocation( + CheckerManager::CheckLocationFunc(checker, _checkLocation)); } }; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 767644a032..16f54ee746 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -137,6 +137,10 @@ public: virtual AnalysisManager &getAnalysisManager() { return AMgr; } + CheckerManager &getCheckerManager() const { + return *AMgr.getCheckerManager(); + } + SValBuilder &getSValBuilder() { return svalBuilder; } TransferFuncs& getTF() { return *TF; } diff --git a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp index ab8d56471c..c072d19a89 100644 --- a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp @@ -92,12 +92,13 @@ void ExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, } if (CO->empty()) { - // If there are no checkers, return early without doing any - // more work. - Dst.insert(Src); + // If there are no checkers, just delegate to the checker manager. + getCheckerManager().runCheckersForStmt(Kind == PreVisitStmtCallback, + Dst, Src, S, *this); return; } + ExplodedNodeSet CheckersV1Dst; ExplodedNodeSet Tmp; ExplodedNodeSet *PrevSet = &Src; unsigned checkersEvaluated = 0; @@ -108,7 +109,7 @@ void ExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, break; ExplodedNodeSet *CurrSet = 0; if (I+1 == E) - CurrSet = &Dst; + CurrSet = &CheckersV1Dst; else { CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; CurrSet->clear(); @@ -144,6 +145,9 @@ void ExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, // Don't autotransition. The CheckerContext objects should do this // automatically. + + getCheckerManager().runCheckersForStmt(Kind == PreVisitStmtCallback, + Dst, CheckersV1Dst, S, *this); } void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg, @@ -152,10 +156,12 @@ void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg, bool isPrevisit) { if (Checkers.empty()) { - Dst.insert(Src); + getCheckerManager().runCheckersForObjCMessage(isPrevisit, Dst, Src, msg, + *this); return; } + ExplodedNodeSet CheckersV1Dst; ExplodedNodeSet Tmp; ExplodedNodeSet *PrevSet = &Src; @@ -163,7 +169,7 @@ void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg, { ExplodedNodeSet *CurrSet = 0; if (I+1 == E) - CurrSet = &Dst; + CurrSet = &CheckersV1Dst; else { CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; CurrSet->clear(); @@ -181,8 +187,8 @@ void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg, PrevSet = CurrSet; } - // Don't autotransition. The CheckerContext objects should do this - // automatically. + getCheckerManager().runCheckersForObjCMessage(isPrevisit, Dst, CheckersV1Dst, + msg, *this); } void ExprEngine::CheckerEvalNilReceiver(const ObjCMessage &msg, @@ -1923,20 +1929,28 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, const GRState* state, SVal location, const void *tag, bool isLoad) { // Early checks for performance reason. - if (location.isUnknown() || Checkers.empty()) { + if (location.isUnknown()) { Dst.Add(Pred); return; } - ExplodedNodeSet Src, Tmp; + ExplodedNodeSet Src; Src.Add(Pred); + if (Checkers.empty()) { + getCheckerManager().runCheckersForLocation(Dst, Src, location, isLoad, S, + state, *this); + return; + } + + ExplodedNodeSet CheckersV1Dst; + ExplodedNodeSet Tmp; ExplodedNodeSet *PrevSet = &Src; for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) { ExplodedNodeSet *CurrSet = 0; if (I+1 == E) - CurrSet = &Dst; + CurrSet = &CheckersV1Dst; else { CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; CurrSet->clear(); @@ -1957,6 +1971,9 @@ void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, // Update which NodeSet is the current one. PrevSet = CurrSet; } + + getCheckerManager().runCheckersForLocation(Dst, CheckersV1Dst, location, + isLoad, S, state, *this); } bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 4f247ea146..5f32bb8f53 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -47,8 +47,9 @@ // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html #include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/CheckerV2.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" @@ -63,45 +64,23 @@ static bool isInitMessage(const ObjCMessage &msg); static bool isSelfVar(SVal location, CheckerContext &C); namespace { -enum SelfFlagEnum { - /// \brief No flag set. - SelfFlag_None = 0x0, - /// \brief Value came from 'self'. - SelfFlag_Self = 0x1, - /// \brief Value came from the result of an initializer (e.g. [super init]). - SelfFlag_InitRes = 0x2 -}; -} - -namespace { -class ObjCSelfInitChecker : public CheckerVisitor { - /// \brief A call receiving a reference to 'self' invalidates the object that - /// 'self' contains. This field keeps the "self flags" assigned to the 'self' - /// object before the call and assign them to the new object that 'self' - /// points to after the call. - SelfFlagEnum preCallSelfFlags; - +class ObjCSelfInitChecker : public CheckerV2< + check::PostObjCMessage, + check::PostStmt, + check::PreStmt, + check::PreStmt, + check::PostStmt, + check::Location > { public: - static void *getTag() { static int tag = 0; return &tag; } - void postVisitObjCMessage(CheckerContext &C, ObjCMessage msg); - void PostVisitObjCIvarRefExpr(CheckerContext &C, const ObjCIvarRefExpr *E); - void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); - void PreVisitGenericCall(CheckerContext &C, const CallExpr *CE); - void PostVisitGenericCall(CheckerContext &C, const CallExpr *CE); - virtual void visitLocation(CheckerContext &C, const Stmt *S, SVal location, - bool isLoad); + void checkPostObjCMessage(ObjCMessage msg, CheckerContext &C) const; + void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const; + void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkLocation(SVal location, bool isLoad, CheckerContext &C) const; }; } // end anonymous namespace -static void RegisterObjCSelfInitChecker(ExprEngine &Eng) { - if (Eng.getContext().getLangOptions().ObjC1) - Eng.registerCheck(new ObjCSelfInitChecker()); -} - -void ento::registerObjCSelfInitChecker(CheckerManager &mgr) { - mgr.addCheckerRegisterFunction(RegisterObjCSelfInitChecker); -} - namespace { class InitSelfBug : public BugType { @@ -113,22 +92,40 @@ public: } // end anonymous namespace +namespace { +enum SelfFlagEnum { + /// \brief No flag set. + SelfFlag_None = 0x0, + /// \brief Value came from 'self'. + SelfFlag_Self = 0x1, + /// \brief Value came from the result of an initializer (e.g. [super init]). + SelfFlag_InitRes = 0x2 +}; +} + typedef llvm::ImmutableMap SelfFlag; namespace { struct CalledInit {}; } +namespace { struct PreCallSelfFlags {}; } namespace clang { namespace ento { template<> struct GRStateTrait : public GRStatePartialTrait { - static void* GDMIndex() { - static int index = 0; - return &index; - } + static void* GDMIndex() { static int index = 0; return &index; } }; template <> struct GRStateTrait : public GRStatePartialTrait { static void *GDMIndex() { static int index = 0; return &index; } }; + + /// \brief A call receiving a reference to 'self' invalidates the object that + /// 'self' contains. This keeps the "self flags" assigned to the 'self' + /// object before the call so we can assign them to the new object that 'self' + /// points to after the call. + template <> + struct GRStateTrait : public GRStatePartialTrait { + static void *GDMIndex() { static int index = 0; return &index; } + }; } } @@ -188,8 +185,8 @@ static void checkForInvalidSelf(const Expr *E, CheckerContext &C, C.EmitReport(report); } -void ObjCSelfInitChecker::postVisitObjCMessage(CheckerContext &C, - ObjCMessage msg) { +void ObjCSelfInitChecker::checkPostObjCMessage(ObjCMessage msg, + CheckerContext &C) const { // When encountering a message that does initialization (init rule), // tag the return value so that we know later on that if self has this value // then it is properly initialized. @@ -219,8 +216,8 @@ void ObjCSelfInitChecker::postVisitObjCMessage(CheckerContext &C, // fails. } -void ObjCSelfInitChecker::PostVisitObjCIvarRefExpr(CheckerContext &C, - const ObjCIvarRefExpr *E) { +void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E, + CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast( C.getCurrentAnalysisContext()->getDecl()))) @@ -231,8 +228,8 @@ void ObjCSelfInitChecker::PostVisitObjCIvarRefExpr(CheckerContext &C, "'[(super or self) init...]'"); } -void ObjCSelfInitChecker::PreVisitReturnStmt(CheckerContext &C, - const ReturnStmt *S) { +void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, + CheckerContext &C) const { // FIXME: A callback should disable checkers at the start of functions. if (!shouldRunOnFunctionOrMethod(dyn_cast( C.getCurrentAnalysisContext()->getDecl()))) @@ -259,40 +256,46 @@ void ObjCSelfInitChecker::PreVisitReturnStmt(CheckerContext &C, // Until we can use inter-procedural analysis, in such a call, transfer the // SelfFlags to the result of the call. -void ObjCSelfInitChecker::PreVisitGenericCall(CheckerContext &C, - const CallExpr *CE) { +void ObjCSelfInitChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { const GRState *state = C.getState(); for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { SVal argV = state->getSVal(*I); if (isSelfVar(argV, C)) { - preCallSelfFlags = getSelfFlags(state->getSVal(cast(argV)), C); + unsigned selfFlags = getSelfFlags(state->getSVal(cast(argV)), C); + C.addTransition(state->set(selfFlags)); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { - preCallSelfFlags = getSelfFlags(argV, C); + unsigned selfFlags = getSelfFlags(argV, C); + C.addTransition(state->set(selfFlags)); return; } } } -void ObjCSelfInitChecker::PostVisitGenericCall(CheckerContext &C, - const CallExpr *CE) { +void ObjCSelfInitChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { const GRState *state = C.getState(); for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { SVal argV = state->getSVal(*I); if (isSelfVar(argV, C)) { - addSelfFlag(state, state->getSVal(cast(argV)), preCallSelfFlags, C); + SelfFlagEnum prevFlags = (SelfFlagEnum)state->get(); + state = state->remove(); + addSelfFlag(state, state->getSVal(cast(argV)), prevFlags, C); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { - addSelfFlag(state, state->getSVal(CE), preCallSelfFlags, C); + SelfFlagEnum prevFlags = (SelfFlagEnum)state->get(); + state = state->remove(); + addSelfFlag(state, state->getSVal(CE), prevFlags, C); return; } } } -void ObjCSelfInitChecker::visitLocation(CheckerContext &C, const Stmt *S, - SVal location, bool isLoad) { +void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, + CheckerContext &C) const { // Tag the result of a load from 'self' so that we can easily know that the // value is the object that 'self' points to. const GRState *state = C.getState(); @@ -354,3 +357,11 @@ static bool isInitializationMethod(const ObjCMethodDecl *MD) { static bool isInitMessage(const ObjCMessage &msg) { return cocoa::deriveNamingConvention(msg.getSelector()) == cocoa::InitRule; } + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerObjCSelfInitChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 1989b822ae..92e97e1bed 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -13,11 +13,17 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/CheckerProvider.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/Analysis/ProgramPoint.h" #include "clang/AST/DeclBase.h" using namespace clang; using namespace ento; +//===----------------------------------------------------------------------===// +// Functions for running checkers for AST traversing.. +//===----------------------------------------------------------------------===// + void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr, BugReporter &BR) { assert(D); @@ -33,39 +39,162 @@ void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr, for (unsigned i = 0, e = DeclCheckers.size(); i != e; ++i) { DeclCheckerInfo &info = DeclCheckers[i]; if (info.IsForDeclFn(D)) - checkers->push_back(std::make_pair(info.Checker, info.CheckFn)); + checkers->push_back(info.CheckFn); } } assert(checkers); for (CachedDeclCheckers::iterator - I = checkers->begin(), E = checkers->end(); I != E; ++I) { - CheckerRef checker = I->first; - CheckDeclFunc fn = I->second; - fn(checker, D, mgr, BR); - } + I = checkers->begin(), E = checkers->end(); I != E; ++I) + (*I)(D, mgr, BR); } void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr, BugReporter &BR) { assert(D && D->hasBody()); - for (unsigned i = 0, e = BodyCheckers.size(); i != e; ++i) { - CheckerRef checker = BodyCheckers[i].first; - CheckDeclFunc fn = BodyCheckers[i].second; - fn(checker, D, mgr, BR); + for (unsigned i = 0, e = BodyCheckers.size(); i != e; ++i) + BodyCheckers[i](D, mgr, BR); +} + +//===----------------------------------------------------------------------===// +// Functions for running checkers for path-sensitive checking. +//===----------------------------------------------------------------------===// + +template +static void runPathSensitiveCheckers(CHECK_CTX checkCtx, + ExplodedNodeSet &Dst, + ExplodedNodeSet &Src) { + + if (checkCtx.Checkers.empty()) { + Dst.insert(Src); + return; + } + + ExplodedNodeSet Tmp; + ExplodedNodeSet *PrevSet = &Src; + + for (typename CHECK_CTX::CheckersTy::const_iterator + I= checkCtx.Checkers.begin(), E= checkCtx.Checkers.end(); I!=E; ++I) { + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) + checkCtx.runChecker(*I, *CurrSet, *NI); + + // Update which NodeSet is the current one. + PrevSet = CurrSet; } } -void CheckerManager::_registerForDecl(CheckerRef checker, CheckDeclFunc checkfn, - HandlesDeclFunc isForDeclFn) { - DeclCheckerInfo info = { checker, checkfn, isForDeclFn }; - DeclCheckers.push_back(info); +namespace { + struct CheckStmtContext { + typedef llvm::SmallVectorImpl CheckersTy; + bool IsPreVisit; + const CheckersTy &Checkers; + const Stmt *S; + ExprEngine &Eng; + + CheckStmtContext(bool isPreVisit, const CheckersTy &checkers, + const Stmt *s, ExprEngine &eng) + : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng) { } + + void runChecker(CheckerManager::CheckStmtFunc checkFn, + ExplodedNodeSet &Dst, ExplodedNode *Pred) { + // FIXME: Remove respondsToCallback from CheckerContext; + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, + IsPreVisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind, 0, S); + checkFn(S, C); + } + }; +} + +/// \brief Run checkers for visiting Stmts. +void CheckerManager::runCheckersForStmt(bool isPreVisit, + ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + const Stmt *S, + ExprEngine &Eng) { + CheckStmtContext C(isPreVisit, *getCachedStmtCheckersFor(S, isPreVisit), + S, Eng); + runPathSensitiveCheckers(C, Dst, Src); +} + +namespace { + struct CheckObjCMessageContext { + typedef std::vector CheckersTy; + bool IsPreVisit; + const CheckersTy &Checkers; + const ObjCMessage &Msg; + ExprEngine &Eng; + + CheckObjCMessageContext(bool isPreVisit, const CheckersTy &checkers, + const ObjCMessage &msg, ExprEngine &eng) + : IsPreVisit(isPreVisit), Checkers(checkers), Msg(msg), Eng(eng) { } + + void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, + ExplodedNodeSet &Dst, ExplodedNode *Pred) { + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, + IsPreVisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind, 0, + Msg.getOriginExpr()); + checkFn(Msg, C); + } + }; +} + +/// \brief Run checkers for visiting obj-c messages. +void CheckerManager::runCheckersForObjCMessage(bool isPreVisit, + ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + const ObjCMessage &msg, + ExprEngine &Eng) { + CheckObjCMessageContext C(isPreVisit, PostObjCMessageCheckers, msg, Eng); + runPathSensitiveCheckers(C, Dst, Src); +} + +namespace { + struct CheckLocationContext { + typedef std::vector CheckersTy; + const CheckersTy &Checkers; + SVal Loc; + bool IsLoad; + const Stmt *S; + const GRState *State; + ExprEngine &Eng; + + CheckLocationContext(const CheckersTy &checkers, + SVal loc, bool isLoad, const Stmt *s, + const GRState *state, ExprEngine &eng) + : Checkers(checkers), Loc(loc), IsLoad(isLoad), S(s), + State(state), Eng(eng) { } + + void runChecker(CheckerManager::CheckLocationFunc checkFn, + ExplodedNodeSet &Dst, ExplodedNode *Pred) { + CheckerContext C(Dst, Eng.getBuilder(), Eng, Pred, checkFn.Checker, + IsLoad ? ProgramPoint::PreLoadKind : + ProgramPoint::PreStoreKind, 0, S, State); + checkFn(Loc, IsLoad, C); + } + }; } -void CheckerManager::_registerForBody(CheckerRef checker, - CheckDeclFunc checkfn) { - BodyCheckers.push_back(std::make_pair(checker, checkfn)); +/// \brief Run checkers for load/store of a location. +void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + SVal location, bool isLoad, + const Stmt *S, + const GRState *state, + ExprEngine &Eng) { + CheckLocationContext C(LocationCheckers, location, isLoad, S, state, Eng); + runPathSensitiveCheckers(C, Dst, Src); } void CheckerManager::registerCheckersToEngine(ExprEngine &eng) { @@ -73,12 +202,76 @@ void CheckerManager::registerCheckersToEngine(ExprEngine &eng) { Funcs[i](eng); } -CheckerManager::~CheckerManager() { - for (unsigned i = 0, e = Checkers.size(); i != e; ++i) { - CheckerRef checker = Checkers[i].first; - Dtor dtor = Checkers[i].second; - dtor(checker); +//===----------------------------------------------------------------------===// +// Internal registration functions for AST traversing. +//===----------------------------------------------------------------------===// + +void CheckerManager::_registerForDecl(CheckDeclFunc checkfn, + HandlesDeclFunc isForDeclFn) { + DeclCheckerInfo info = { checkfn, isForDeclFn }; + DeclCheckers.push_back(info); +} + +void CheckerManager::_registerForBody(CheckDeclFunc checkfn) { + BodyCheckers.push_back(checkfn); +} + +//===----------------------------------------------------------------------===// +// Internal registration functions for path-sensitive checking. +//===----------------------------------------------------------------------===// + +void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn, + HandlesStmtFunc isForStmtFn) { + StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/true }; + StmtCheckers.push_back(info); +} +void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn, + HandlesStmtFunc isForStmtFn) { + StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/false }; + StmtCheckers.push_back(info); +} + +void CheckerManager::_registerForPreObjCMessage(CheckObjCMessageFunc checkfn) { + PreObjCMessageCheckers.push_back(checkfn); +} +void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) { + PostObjCMessageCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) { + LocationCheckers.push_back(checkfn); +} + +//===----------------------------------------------------------------------===// +// Implementation details. +//===----------------------------------------------------------------------===// + +CheckerManager::CachedStmtCheckers * +CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) { + assert(S); + + CachedStmtCheckersKey key(S->getStmtClass(), isPreVisit); + CachedStmtCheckers *checkers = 0; + CachedStmtCheckersMapTy::iterator CCI = CachedStmtCheckersMap.find(key); + if (CCI != CachedStmtCheckersMap.end()) { + checkers = &(CCI->second); + } else { + // Find the checkers that should run for this Stmt and cache them. + checkers = &CachedStmtCheckersMap[key]; + for (unsigned i = 0, e = StmtCheckers.size(); i != e; ++i) { + StmtCheckerInfo &info = StmtCheckers[i]; + if (info.IsPreVisit == isPreVisit && info.IsForStmtFn(S)) + checkers->push_back(info.CheckFn); + } } + + assert(checkers); + return checkers; +} + +CheckerManager::~CheckerManager() { + for (unsigned i = 0, e = CheckerDtors.size(); i != e; ++i) + CheckerDtors[i](); } // Anchor for the vtable.