diff options
-rw-r--r-- | include/clang/StaticAnalyzer/Core/CheckerManager.h | 230 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/CheckerV2.h | 90 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 4 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/ExprEngine.cpp | 39 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp | 123 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CheckerManager.cpp | 237 |
6 files changed, 615 insertions, 108 deletions
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 <vector> 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 <typename P1=VoidCheckerFnParm, typename P2=VoidCheckerFnParm, + typename P3=VoidCheckerFnParm, typename P4=VoidCheckerFnParm> +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 <typename P1, typename P2, typename P3> +class CheckerFn<P1, P2, P3, VoidCheckerFnParm> { + 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 <typename P1, typename P2> +class CheckerFn<P1, P2, VoidCheckerFnParm, VoidCheckerFnParm> { + 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<VoidCheckerFnParm, VoidCheckerFnParm, VoidCheckerFnParm, + VoidCheckerFnParm> { + 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 <typename CHECKER> void registerChecker() { CHECKER *checker = new CHECKER(); - Checkers.push_back(std::pair<CheckerRef, Dtor>(checker, destruct<CHECKER>)); + CheckerDtors.push_back(CheckerDtor(checker, destruct<CHECKER>)); 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<const Decl *, AnalysisManager&, BugReporter &> + CheckDeclFunc; + typedef CheckerFn<const Stmt *, CheckerContext &> 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<const ObjCMessage &, CheckerContext &> CheckObjCMessageFunc; + typedef CheckerFn<const SVal &/*location*/, bool/*isLoad*/, CheckerContext &> + 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 <typename CHECKER> static void destruct(void *obj) { delete static_cast<CHECKER *>(obj); } + std::vector<CheckerDtor> CheckerDtors; + std::vector<RegisterToEngFunc> Funcs; struct DeclCheckerInfo { - CheckerRef Checker; CheckDeclFunc CheckFn; HandlesDeclFunc IsForDeclFn; }; std::vector<DeclCheckerInfo> DeclCheckers; - std::vector<std::pair<CheckerRef, CheckDeclFunc> > BodyCheckers; - - typedef void (*Dtor)(void *); - std::vector<std::pair<CheckerRef, Dtor> > Checkers; + std::vector<CheckDeclFunc> BodyCheckers; - typedef llvm::SmallVector<std::pair<CheckerRef, CheckDeclFunc>, 4> - CachedDeclCheckers; + typedef llvm::SmallVector<CheckDeclFunc, 4> CachedDeclCheckers; typedef llvm::DenseMap<unsigned, CachedDeclCheckers> CachedDeclCheckersMapTy; CachedDeclCheckersMapTy CachedDeclCheckersMap; + + struct StmtCheckerInfo { + CheckStmtFunc CheckFn; + HandlesStmtFunc IsForStmtFn; + bool IsPreVisit; + }; + std::vector<StmtCheckerInfo> 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<CachedStmtCheckersKey>; + + typedef llvm::SmallVector<CheckStmtFunc, 4> CachedStmtCheckers; + typedef llvm::DenseMap<CachedStmtCheckersKey, CachedStmtCheckers> + CachedStmtCheckersMapTy; + CachedStmtCheckersMapTy CachedStmtCheckersMap; + + CachedStmtCheckers *getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit); + + std::vector<CheckObjCMessageFunc> PreObjCMessageCheckers; + std::vector<CheckObjCMessageFunc> PostObjCMessageCheckers; + + std::vector<CheckLocationFunc> 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<clang::ento::CheckerManager::CachedStmtCheckersKey> { + 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 <typename CHECKER> static void _register(CHECKER *checker, CheckerManager &mgr) { - mgr._registerForDecl(checker, _checkDecl<CHECKER>, _handlesDecl); + mgr._registerForDecl(CheckerManager::CheckDeclFunc(checker, + _checkDecl<CHECKER>), + _handlesDecl); } }; @@ -55,7 +57,91 @@ class ASTCodeBody { public: template <typename CHECKER> static void _register(CHECKER *checker, CheckerManager &mgr) { - mgr._registerForBody(checker, _checkBody<CHECKER>); + mgr._registerForBody(CheckerManager::CheckDeclFunc(checker, + _checkBody<CHECKER>)); + } +}; + +template <typename STMT> +class PreStmt { + template <typename CHECKER> + static void _checkStmt(void *checker, const Stmt *S, CheckerContext &C) { + ((const CHECKER *)checker)->checkPreStmt(llvm::cast<STMT>(S), C); + } + + static bool _handlesStmt(const Stmt *S) { + return llvm::isa<STMT>(S); + } +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPreStmt(CheckerManager::CheckStmtFunc(checker, + _checkStmt<CHECKER>), + _handlesStmt); + } +}; + +template <typename STMT> +class PostStmt { + template <typename CHECKER> + static void _checkStmt(void *checker, const Stmt *S, CheckerContext &C) { + ((const CHECKER *)checker)->checkPostStmt(llvm::cast<STMT>(S), C); + } + + static bool _handlesStmt(const Stmt *S) { + return llvm::isa<STMT>(S); + } +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPostStmt(CheckerManager::CheckStmtFunc(checker, + _checkStmt<CHECKER>), + _handlesStmt); + } +}; + +class PreObjCMessage { + template <typename CHECKER> + static void _checkObjCMessage(void *checker, const ObjCMessage &msg, + CheckerContext &C) { + ((const CHECKER *)checker)->checkPreObjCMessage(msg, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPreObjCMessage( + CheckerManager::CheckObjCMessageFunc(checker, _checkObjCMessage<CHECKER>)); + } +}; + +class PostObjCMessage { + template <typename CHECKER> + static void _checkObjCMessage(void *checker, const ObjCMessage &msg, + CheckerContext &C) { + ((const CHECKER *)checker)->checkPostObjCMessage(msg, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForPostObjCMessage( + CheckerManager::CheckObjCMessageFunc(checker, _checkObjCMessage<CHECKER>)); + } +}; + +class Location { + template <typename CHECKER> + static void _checkLocation(void *checker, const SVal &location, bool isLoad, + CheckerContext &C) { + ((const CHECKER *)checker)->checkLocation(location, isLoad, C); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForLocation( + CheckerManager::CheckLocationFunc(checker, _checkLocation<CHECKER>)); } }; 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<ObjCSelfInitChecker> { - /// \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<ObjCIvarRefExpr>, + check::PreStmt<ReturnStmt>, + check::PreStmt<CallExpr>, + check::PostStmt<CallExpr>, + 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<SymbolRef, unsigned> SelfFlag; namespace { struct CalledInit {}; } +namespace { struct PreCallSelfFlags {}; } namespace clang { namespace ento { template<> struct GRStateTrait<SelfFlag> : public GRStatePartialTrait<SelfFlag> { - static void* GDMIndex() { - static int index = 0; - return &index; - } + static void* GDMIndex() { static int index = 0; return &index; } }; template <> struct GRStateTrait<CalledInit> : public GRStatePartialTrait<bool> { 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<PreCallSelfFlags> : public GRStatePartialTrait<unsigned> { + 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<NamedDecl>( 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<NamedDecl>( 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<Loc>(argV)), C); + unsigned selfFlags = getSelfFlags(state->getSVal(cast<Loc>(argV)), C); + C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { - preCallSelfFlags = getSelfFlags(argV, C); + unsigned selfFlags = getSelfFlags(argV, C); + C.addTransition(state->set<PreCallSelfFlags>(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<Loc>(argV)), preCallSelfFlags, C); + SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); + state = state->remove<PreCallSelfFlags>(); + addSelfFlag(state, state->getSVal(cast<Loc>(argV)), prevFlags, C); return; } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { - addSelfFlag(state, state->getSVal(CE), preCallSelfFlags, C); + SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); + state = state->remove<PreCallSelfFlags>(); + 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<ObjCSelfInitChecker>(); +} 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 <typename CHECK_CTX> +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<CheckerManager::CheckStmtFunc> 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<CheckerManager::CheckObjCMessageFunc> 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, |