diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/StaticAnalyzer/CFRefCount.cpp | 125 | ||||
-rw-r--r-- | lib/StaticAnalyzer/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | 57 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp | 91 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/ExprEngine.cpp | 57 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp | 14 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp | 16 | ||||
-rw-r--r-- | lib/StaticAnalyzer/ObjCMessage.cpp | 99 |
8 files changed, 289 insertions, 171 deletions
diff --git a/lib/StaticAnalyzer/CFRefCount.cpp b/lib/StaticAnalyzer/CFRefCount.cpp index d4bed35317..5cc4a3c3ee 100644 --- a/lib/StaticAnalyzer/CFRefCount.cpp +++ b/lib/StaticAnalyzer/CFRefCount.cpp @@ -40,14 +40,15 @@ using llvm::StrInStrNoCase; namespace { class InstanceReceiver { - const ObjCMessageExpr *ME; + ObjCMessage Msg; const LocationContext *LC; public: - InstanceReceiver(const ObjCMessageExpr *me = 0, - const LocationContext *lc = 0) : ME(me), LC(lc) {} + InstanceReceiver() : LC(0) { } + InstanceReceiver(const ObjCMessage &msg, + const LocationContext *lc = 0) : Msg(msg), LC(lc) {} bool isValid() const { - return ME && ME->isInstanceMessage(); + return Msg.isValid() && Msg.isInstanceMessage(); } operator bool() const { return isValid(); @@ -57,7 +58,7 @@ public: assert(isValid()); // We have an expression for the receiver? Fetch the value // of that expression. - if (const Expr *Ex = ME->getInstanceReceiver()) + if (const Expr *Ex = Msg.getInstanceReceiver()) return state->getSValAsScalarOrLoc(Ex); // Otherwise we are sending a message to super. In this case the @@ -70,11 +71,11 @@ public: SourceRange getSourceRange() const { assert(isValid()); - if (const Expr *Ex = ME->getInstanceReceiver()) + if (const Expr *Ex = Msg.getInstanceReceiver()) return Ex->getSourceRange(); // Otherwise we are sending a message to super. - SourceLocation L = ME->getSuperLoc(); + SourceLocation L = Msg.getSuperLoc(); assert(L.isValid()); return SourceRange(L, L); } @@ -798,14 +799,14 @@ public: RetainSummary* getSummary(const FunctionDecl* FD); - RetainSummary *getInstanceMethodSummary(const ObjCMessageExpr *ME, + RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg, const GRState *state, const LocationContext *LC); - RetainSummary* getInstanceMethodSummary(const ObjCMessageExpr* ME, + RetainSummary* getInstanceMethodSummary(const ObjCMessage &msg, const ObjCInterfaceDecl* ID) { - return getInstanceMethodSummary(ME->getSelector(), 0, - ID, ME->getMethodDecl(), ME->getType()); + return getInstanceMethodSummary(msg.getSelector(), 0, + ID, msg.getMethodDecl(), msg.getType(Ctx)); } RetainSummary* getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName, @@ -818,23 +819,15 @@ public: const ObjCMethodDecl *MD, QualType RetTy); - RetainSummary *getClassMethodSummary(const ObjCMessageExpr *ME) { - ObjCInterfaceDecl *Class = 0; - switch (ME->getReceiverKind()) { - case ObjCMessageExpr::Class: - case ObjCMessageExpr::SuperClass: - Class = ME->getReceiverInterface(); - break; - - case ObjCMessageExpr::Instance: - case ObjCMessageExpr::SuperInstance: - break; - } + RetainSummary *getClassMethodSummary(const ObjCMessage &msg) { + const ObjCInterfaceDecl *Class = 0; + if (!msg.isInstanceMessage()) + Class = msg.getReceiverInterface(); - return getClassMethodSummary(ME->getSelector(), + return getClassMethodSummary(msg.getSelector(), Class? Class->getIdentifier() : 0, Class, - ME->getMethodDecl(), ME->getType()); + msg.getMethodDecl(), msg.getType(Ctx)); } /// getMethodSummary - This version of getMethodSummary is used to query @@ -1310,13 +1303,13 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, } RetainSummary* -RetainSummaryManager::getInstanceMethodSummary(const ObjCMessageExpr *ME, +RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg, const GRState *state, const LocationContext *LC) { // We need the type-information of the tracked receiver object // Retrieve it from the state. - const Expr *Receiver = ME->getInstanceReceiver(); + const Expr *Receiver = msg.getInstanceReceiver(); const ObjCInterfaceDecl* ID = 0; // FIXME: Is this really working as expected? There are cases where @@ -1344,12 +1337,12 @@ RetainSummaryManager::getInstanceMethodSummary(const ObjCMessageExpr *ME, } } else { // FIXME: Hack for 'super'. - ID = ME->getReceiverInterface(); + ID = msg.getReceiverInterface(); } // FIXME: The receiver could be a reference to a class, meaning that // we should use the class method. - RetainSummary *Summ = getInstanceMethodSummary(ME, ID); + RetainSummary *Summ = getInstanceMethodSummary(msg, ID); // Special-case: are we sending a mesage to "self"? // This is a hack. When we have full-IP this should be removed. @@ -1693,10 +1686,10 @@ public: ExprEngine& Eng, StmtNodeBuilder& Builder, const Expr* Ex, + const CallOrObjCMessage &callOrMsg, InstanceReceiver Receiver, const RetainSummary& Summ, const MemRegion *Callee, - ConstExprIterator arg_beg, ConstExprIterator arg_end, ExplodedNode* Pred, const GRState *state); virtual void evalCall(ExplodedNodeSet& Dst, @@ -1706,12 +1699,12 @@ public: ExplodedNode* Pred); - virtual void evalObjCMessageExpr(ExplodedNodeSet& Dst, - ExprEngine& Engine, - StmtNodeBuilder& Builder, - const ObjCMessageExpr* ME, - ExplodedNode* Pred, - const GRState *state); + virtual void evalObjCMessage(ExplodedNodeSet& Dst, + ExprEngine& Engine, + StmtNodeBuilder& Builder, + ObjCMessage msg, + ExplodedNode* Pred, + const GRState *state); // Stores. virtual void evalBind(StmtNodeBuilderRef& B, SVal location, SVal val); @@ -2477,16 +2470,14 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, ExprEngine& Eng, StmtNodeBuilder& Builder, const Expr* Ex, + const CallOrObjCMessage &callOrMsg, InstanceReceiver Receiver, const RetainSummary& Summ, const MemRegion *Callee, - ConstExprIterator arg_beg, - ConstExprIterator arg_end, ExplodedNode* Pred, const GRState *state) { // Evaluate the effect of the arguments. RefVal::Kind hasErr = (RefVal::Kind) 0; - unsigned idx = 0; SourceRange ErrorRange; SymbolRef ErrorSym = 0; @@ -2498,8 +2489,8 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, // done an invalidation pass. llvm::DenseSet<SymbolRef> WhitelistedSymbols; - for (ConstExprIterator I = arg_beg; I != arg_end; ++I, ++idx) { - SVal V = state->getSValAsScalarOrLoc(*I); + for (unsigned idx = 0, e = callOrMsg.getNumArgs(); idx != e; ++idx) { + SVal V = callOrMsg.getArgSValAsScalarOrLoc(idx); SymbolRef Sym = V.getAsLocSymbol(); if (Sym) @@ -2507,7 +2498,7 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, WhitelistedSymbols.insert(Sym); state = Update(state, Sym, *T, Summ.getArg(idx), hasErr); if (hasErr) { - ErrorRange = (*I)->getSourceRange(); + ErrorRange = callOrMsg.getArgSourceRange(idx); ErrorSym = Sym; break; } @@ -2650,19 +2641,7 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, // FIXME: We eventually should handle structs and other compound types // that are returned by value. - QualType T = Ex->getType(); - - // For CallExpr, use the result type to know if it returns a reference. - if (const CallExpr *CE = dyn_cast<CallExpr>(Ex)) { - const Expr *Callee = CE->getCallee(); - if (const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl()) - T = FD->getResultType(); - } - else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(Ex)) { - if (const ObjCMethodDecl *MD = ME->getMethodDecl()) - T = MD->getResultType(); - } - + QualType T = callOrMsg.getResultType(Eng.getContext()); if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) { unsigned Count = Builder.getCurrentBlockCount(); SValBuilder &svalBuilder = Eng.getSValBuilder(); @@ -2675,9 +2654,8 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, case RetEffect::Alias: { unsigned idx = RE.getIndex(); - assert (arg_end >= arg_beg); - assert (idx < (unsigned) (arg_end - arg_beg)); - SVal V = state->getSValAsScalarOrLoc(*(arg_beg+idx)); + assert (idx < callOrMsg.getNumArgs()); + SVal V = callOrMsg.getArgSValAsScalarOrLoc(idx); state = state->BindExpr(Ex, V, false); break; } @@ -2756,25 +2734,28 @@ void CFRefCount::evalCall(ExplodedNodeSet& Dst, } assert(Summ); - evalSummary(Dst, Eng, Builder, CE, 0, *Summ, L.getAsRegion(), - CE->arg_begin(), CE->arg_end(), Pred, Builder.GetState(Pred)); + evalSummary(Dst, Eng, Builder, CE, + CallOrObjCMessage(CE, Builder.GetState(Pred)), + InstanceReceiver(), *Summ,L.getAsRegion(), + Pred, Builder.GetState(Pred)); } -void CFRefCount::evalObjCMessageExpr(ExplodedNodeSet& Dst, - ExprEngine& Eng, - StmtNodeBuilder& Builder, - const ObjCMessageExpr* ME, - ExplodedNode* Pred, - const GRState *state) { +void CFRefCount::evalObjCMessage(ExplodedNodeSet& Dst, + ExprEngine& Eng, + StmtNodeBuilder& Builder, + ObjCMessage msg, + ExplodedNode* Pred, + const GRState *state) { RetainSummary *Summ = - ME->isInstanceMessage() - ? Summaries.getInstanceMethodSummary(ME, state,Pred->getLocationContext()) - : Summaries.getClassMethodSummary(ME); + msg.isInstanceMessage() + ? Summaries.getInstanceMethodSummary(msg, state,Pred->getLocationContext()) + : Summaries.getClassMethodSummary(msg); assert(Summ && "RetainSummary is null"); - evalSummary(Dst, Eng, Builder, ME, - InstanceReceiver(ME, Pred->getLocationContext()), *Summ, NULL, - ME->arg_begin(), ME->arg_end(), Pred, state); + evalSummary(Dst, Eng, Builder, msg.getOriginExpr(), + CallOrObjCMessage(msg, Builder.GetState(Pred)), + InstanceReceiver(msg, Pred->getLocationContext()), *Summ, NULL, + Pred, state); } namespace { diff --git a/lib/StaticAnalyzer/CMakeLists.txt b/lib/StaticAnalyzer/CMakeLists.txt index cf81e1d5ad..352138599e 100644 --- a/lib/StaticAnalyzer/CMakeLists.txt +++ b/lib/StaticAnalyzer/CMakeLists.txt @@ -24,6 +24,7 @@ add_clang_library(clangStaticAnalyzerCore HTMLDiagnostics.cpp ManagerRegistry.cpp MemRegion.cpp + ObjCMessage.cpp PathDiagnostic.cpp PlistDiagnostics.cpp RangeConstraintManager.cpp diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 3910196265..8fa7e24cab 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -42,14 +42,14 @@ public: // Utility functions. //===----------------------------------------------------------------------===// -static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) { - if (ObjCInterfaceDecl *ID = ME->getReceiverInterface()) +static const ObjCInterfaceType* GetReceiverType(const ObjCMessage &msg) { + if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) return ID->getTypeForDecl()->getAs<ObjCInterfaceType>(); return NULL; } -static const char* GetReceiverNameType(const ObjCMessageExpr* ME) { - if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME)) +static const char* GetReceiverNameType(const ObjCMessage &msg) { + if (const ObjCInterfaceType *ReceiverType = GetReceiverType(msg)) return ReceiverType->getDecl()->getIdentifier()->getNameStart(); return NULL; } @@ -69,16 +69,16 @@ static inline bool isNil(SVal X) { namespace { class NilArgChecker : public CheckerVisitor<NilArgChecker> { APIMisuse *BT; - void WarnNilArg(CheckerContext &C, const ObjCMessageExpr* ME, unsigned Arg); + void WarnNilArg(CheckerContext &C, const ObjCMessage &msg, unsigned Arg); public: NilArgChecker() : BT(0) {} static void *getTag() { static int x = 0; return &x; } - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); + void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); }; } void NilArgChecker::WarnNilArg(CheckerContext &C, - const clang::ObjCMessageExpr *ME, + const ObjCMessage &msg, unsigned int Arg) { if (!BT) @@ -87,24 +87,24 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, if (ExplodedNode *N = C.generateSink()) { llvm::SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); - os << "Argument to '" << GetReceiverNameType(ME) << "' method '" - << ME->getSelector().getAsString() << "' cannot be nil"; + os << "Argument to '" << GetReceiverNameType(msg) << "' method '" + << msg.getSelector().getAsString() << "' cannot be nil"; RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); - R->addRange(ME->getArg(Arg)->getSourceRange()); + R->addRange(msg.getArgSourceRange(Arg)); C.EmitReport(R); } } -void NilArgChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) +void NilArgChecker::preVisitObjCMessage(CheckerContext &C, + ObjCMessage msg) { - const ObjCInterfaceType *ReceiverType = GetReceiverType(ME); + const ObjCInterfaceType *ReceiverType = GetReceiverType(msg); if (!ReceiverType) return; if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) { - Selector S = ME->getSelector(); + Selector S = msg.getSelector(); if (S.isUnarySelector()) return; @@ -127,8 +127,8 @@ void NilArgChecker::PreVisitObjCMessageExpr(CheckerContext &C, Name == "compare:options:range:locale:" || Name == "componentsSeparatedByCharactersInSet:" || Name == "initWithFormat:") { - if (isNil(C.getState()->getSVal(ME->getArg(0)))) - WarnNilArg(C, ME, 0); + if (isNil(msg.getArgSVal(0, C.getState()))) + WarnNilArg(C, msg, 0); } } } @@ -441,12 +441,12 @@ public: static void *getTag() { static int x = 0; return &x; } - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); + void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); }; } -void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) { +void ClassReleaseChecker::preVisitObjCMessage(CheckerContext &C, + ObjCMessage msg) { if (!BT) { BT = new APIMisuse("message incorrectly sent to class instead of class " @@ -459,21 +459,12 @@ void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, drainS = GetNullarySelector("drain", Ctx); } - ObjCInterfaceDecl *Class = 0; - - switch (ME->getReceiverKind()) { - case ObjCMessageExpr::Class: - Class = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface(); - break; - case ObjCMessageExpr::SuperClass: - Class = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface(); - break; - case ObjCMessageExpr::Instance: - case ObjCMessageExpr::SuperInstance: + if (msg.isInstanceMessage()) return; - } + const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); + assert(Class); - Selector S = ME->getSelector(); + Selector S = msg.getSelector(); if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) return; @@ -486,7 +477,7 @@ void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, << "' and not the class directly"; RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); - report->addRange(ME->getSourceRange()); + report->addRange(msg.getSourceRange()); C.EmitReport(report); } } diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 2998406da0..e6a40bb597 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -41,19 +41,21 @@ public: } void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); - bool evalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME); + void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); + bool evalNilReceiver(CheckerContext &C, ObjCMessage msg); private: - bool PreVisitProcessArg(CheckerContext &C, const Expr *Ex, - const char *BT_desc, BugType *&BT); + void PreVisitProcessArgs(CheckerContext &C, CallOrObjCMessage callOrMsg, + const char *BT_desc, BugType *&BT); + bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange, + const Expr *argEx, const char *BT_desc, BugType *&BT); void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); - void emitNilReceiverBug(CheckerContext &C, const ObjCMessageExpr *ME, + void emitNilReceiverBug(CheckerContext &C, const ObjCMessage &msg, ExplodedNode *N); void HandleNilReceiver(CheckerContext &C, const GRState *state, - const ObjCMessageExpr *ME); + ObjCMessage msg); void LazyInit_BT(const char *desc, BugType *&BT) { if (!BT) @@ -78,21 +80,32 @@ void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, C.EmitReport(R); } +void CallAndMessageChecker::PreVisitProcessArgs(CheckerContext &C, + CallOrObjCMessage callOrMsg, + const char *BT_desc, + BugType *&BT) { + for (unsigned i = 0, e = callOrMsg.getNumArgs(); i != e; ++i) + if (PreVisitProcessArg(C, callOrMsg.getArgSVal(i), + callOrMsg.getArgSourceRange(i), callOrMsg.getArg(i), + BT_desc, BT)) + return; +} + bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, - const Expr *Ex, + SVal V, SourceRange argRange, + const Expr *argEx, const char *BT_desc, BugType *&BT) { - const SVal &V = C.getState()->getSVal(Ex); - if (V.isUndef()) { if (ExplodedNode *N = C.generateSink()) { LazyInit_BT(BT_desc, BT); // Generate a report for this bug. EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); - R->addRange(Ex->getSourceRange()); - R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + R->addRange(argRange); + if (argEx) + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, argEx); C.EmitReport(R); } return true; @@ -172,7 +185,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, // Generate a report for this bug. EnhancedBugReport *R = new EnhancedBugReport(*BT, os.str(), N); - R->addRange(Ex->getSourceRange()); + R->addRange(argRange); // FIXME: enhance track back for uninitialized value for arbitrary // memregions @@ -206,21 +219,18 @@ void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C, EmitBadCall(BT_call_null, C, CE); } - for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); - I != E; ++I) - if (PreVisitProcessArg(C, *I, - "Function call argument is an uninitialized value", - BT_call_arg)) - return; + PreVisitProcessArgs(C, CallOrObjCMessage(CE, C.getState()), + "Function call argument is an uninitialized value", + BT_call_arg); } -void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) { +void CallAndMessageChecker::preVisitObjCMessage(CheckerContext &C, + ObjCMessage msg) { const GRState *state = C.getState(); // FIXME: Handle 'super'? - if (const Expr *receiver = ME->getInstanceReceiver()) + if (const Expr *receiver = msg.getInstanceReceiver()) if (state->getSVal(receiver).isUndef()) { if (ExplodedNode *N = C.generateSink()) { if (!BT_msg_undef) @@ -237,22 +247,19 @@ void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C, } // Check for any arguments that are uninitialized/undefined. - for (ObjCMessageExpr::const_arg_iterator I = ME->arg_begin(), - E = ME->arg_end(); I != E; ++I) - if (PreVisitProcessArg(C, *I, - "Argument in message expression " - "is an uninitialized value", BT_msg_arg)) - return; + PreVisitProcessArgs(C, CallOrObjCMessage(msg, state), + "Argument in message expression " + "is an uninitialized value", BT_msg_arg); } bool CallAndMessageChecker::evalNilReceiver(CheckerContext &C, - const ObjCMessageExpr *ME) { - HandleNilReceiver(C, C.getState(), ME); + ObjCMessage msg) { + HandleNilReceiver(C, C.getState(), msg); return true; // Nil receiver is not handled elsewhere. } void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, - const ObjCMessageExpr *ME, + const ObjCMessage &msg, ExplodedNode *N) { if (!BT_msg_ret) @@ -262,12 +269,12 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, llvm::SmallString<200> buf; llvm::raw_svector_ostream os(buf); - os << "The receiver of message '" << ME->getSelector().getAsString() + os << "The receiver of message '" << msg.getSelector().getAsString() << "' is nil and returns a value of type '" - << ME->getType().getAsString() << "' that will be garbage"; + << msg.getType(C.getASTContext()).getAsString() << "' that will be garbage"; EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N); - if (const Expr *receiver = ME->getInstanceReceiver()) { + if (const Expr *receiver = msg.getInstanceReceiver()) { report->addRange(receiver->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, receiver); @@ -284,22 +291,22 @@ static bool supportsNilWithFloatRet(const llvm::Triple &triple) { void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, const GRState *state, - const ObjCMessageExpr *ME) { + ObjCMessage msg) { + ASTContext &Ctx = C.getASTContext(); // Check the return type of the message expression. A message to nil will // return different values depending on the return type and the architecture. - QualType RetTy = ME->getType(); + QualType RetTy = msg.getType(Ctx); - ASTContext &Ctx = C.getASTContext(); CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); if (CanRetTy->isStructureOrClassType()) { // FIXME: At some point we shouldn't rely on isConsumedExpr(), but instead // have the "use of undefined value" be smarter about where the // undefined value came from. - if (C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { + if (C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())){ if (ExplodedNode* N = C.generateSink(state)) - emitNilReceiverBug(C, ME, N); + emitNilReceiverBug(C, msg, N); return; } @@ -311,7 +318,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, // Other cases: check if the return type is smaller than void*. if (CanRetTy != Ctx.VoidTy && - C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { + C.getPredecessor()->getParentMap().isConsumedExpr(msg.getOriginExpr())) { // Compute: sizeof(void *) and sizeof(return type) const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); @@ -324,7 +331,7 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy))) { if (ExplodedNode* N = C.generateSink(state)) - emitNilReceiverBug(C, ME, N); + emitNilReceiverBug(C, msg, N); return; } @@ -341,8 +348,8 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, // it most likely isn't nil. We should assume the semantics // of this case unless we have *a lot* more knowledge. // - SVal V = C.getSValBuilder().makeZeroVal(ME->getType()); - C.generateNode(state->BindExpr(ME, V)); + SVal V = C.getSValBuilder().makeZeroVal(msg.getType(Ctx)); + C.generateNode(state->BindExpr(msg.getOriginExpr(), V)); return; } diff --git a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp index a022562a12..6932f4a717 100644 --- a/lib/StaticAnalyzer/Checkers/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp @@ -145,10 +145,49 @@ void ExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, // automatically. } -void ExprEngine::CheckerEvalNilReceiver(const ObjCMessageExpr *ME, - ExplodedNodeSet &Dst, - const GRState *state, - ExplodedNode *Pred) { +void ExprEngine::CheckerVisitObjCMessage(const ObjCMessage &msg, + ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + bool isPrevisit) { + + if (Checkers.empty()) { + Dst.insert(Src); + return; + } + + 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; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) + checker->GR_visitObjCMessage(*CurrSet, *Builder, *this, msg, + *NI, tag, isPrevisit); + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } + + // Don't autotransition. The CheckerContext objects should do this + // automatically. +} + +void ExprEngine::CheckerEvalNilReceiver(const ObjCMessage &msg, + ExplodedNodeSet &Dst, + const GRState *state, + ExplodedNode *Pred) { bool evaluated = false; ExplodedNodeSet DstTmp; @@ -156,7 +195,7 @@ void ExprEngine::CheckerEvalNilReceiver(const ObjCMessageExpr *ME, void *tag = I->first; Checker *checker = I->second; - if (checker->GR_evalNilReceiver(DstTmp, *Builder, *this, ME, Pred, state, + if (checker->GR_evalNilReceiver(DstTmp, *Builder, *this, msg, Pred, state, tag)) { evaluated = true; break; @@ -2263,7 +2302,7 @@ void ExprEngine::VisitObjCMessageExpr(const ObjCMessageExpr* ME, // Now that the arguments are processed, handle the previsits checks. ExplodedNodeSet DstPrevisit; - CheckerVisit(ME, DstPrevisit, ArgsEvaluated, PreVisitStmtCallback); + CheckerVisitObjCMessage(ME, DstPrevisit, ArgsEvaluated, /*isPreVisit=*/true); // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; @@ -2305,7 +2344,7 @@ void ExprEngine::VisitObjCMessageExpr(const ObjCMessageExpr* ME, Builder->BuildSinks = true; // Dispatch to plug-in transfer function. - evalObjCMessageExpr(dstEval, ME, Pred, notNilState); + evalObjCMessage(dstEval, ME, Pred, notNilState); } else if (ObjCInterfaceDecl *Iface = ME->getReceiverInterface()) { IdentifierInfo* ClsName = Iface->getIdentifier(); @@ -2353,7 +2392,7 @@ void ExprEngine::VisitObjCMessageExpr(const ObjCMessageExpr* ME, Builder->BuildSinks = true; // Dispatch to plug-in transfer function. - evalObjCMessageExpr(dstEval, ME, Pred, Builder->GetState(Pred)); + evalObjCMessage(dstEval, ME, Pred, Builder->GetState(Pred)); } // Handle the case where no nodes where generated. Auto-generate that @@ -2365,7 +2404,7 @@ void ExprEngine::VisitObjCMessageExpr(const ObjCMessageExpr* ME, // Finally, perform the post-condition check of the ObjCMessageExpr and store // the created nodes in 'Dst'. - CheckerVisit(ME, Dst, dstEval, PostVisitStmtCallback); + CheckerVisitObjCMessage(ME, Dst, dstEval, /*isPreVisit=*/false); } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index 6ef242ba42..b1b4773aee 100644 --- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -39,7 +39,7 @@ public: return &x; } - void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); + void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg); }; } // end anonymous namespace @@ -54,10 +54,10 @@ void ento::RegisterNSAutoreleasePoolChecks(ExprEngine &Eng) { } void -NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *ME) { +NSAutoreleasePoolChecker::preVisitObjCMessage(CheckerContext &C, + ObjCMessage msg) { - const Expr *receiver = ME->getInstanceReceiver(); + const Expr *receiver = msg.getInstanceReceiver(); if (!receiver) return; @@ -75,13 +75,13 @@ NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C, return; // Sending 'release' message? - if (ME->getSelector() != releaseS) + if (msg.getSelector() != releaseS) return; - SourceRange R = ME->getSourceRange(); + SourceRange R = msg.getSourceRange(); C.getBugReporter().EmitBasicReport("Use -drain instead of -release", "API Upgrade (Apple)", "Use -drain instead of -release when using NSAutoreleasePool " - "and garbage collection", ME->getLocStart(), &R, 1); + "and garbage collection", R.getBegin(), &R, 1); } diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 9eb6b19ec7..c887ac86ef 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -58,7 +58,7 @@ using namespace ento; static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND); static bool isInitializationMethod(const ObjCMethodDecl *MD); -static bool isInitMessage(const ObjCMessageExpr *E); +static bool isInitMessage(const ObjCMessage &msg); static bool isSelfVar(SVal location, CheckerContext &C); namespace { @@ -82,7 +82,7 @@ class ObjCSelfInitChecker : public CheckerVisitor<ObjCSelfInitChecker> { public: static void *getTag() { static int tag = 0; return &tag; } - void PostVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *E); + 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); @@ -176,8 +176,8 @@ static void checkForInvalidSelf(const Expr *E, CheckerContext &C, C.EmitReport(report); } -void ObjCSelfInitChecker::PostVisitObjCMessageExpr(CheckerContext &C, - const ObjCMessageExpr *E) { +void ObjCSelfInitChecker::postVisitObjCMessage(CheckerContext &C, + ObjCMessage msg) { // 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. @@ -187,10 +187,10 @@ void ObjCSelfInitChecker::PostVisitObjCMessageExpr(CheckerContext &C, C.getCurrentAnalysisContext()->getDecl()))) return; - if (isInitMessage(E)) { + if (isInitMessage(msg)) { // Tag the return value as the result of an initializer. const GRState *state = C.getState(); - SVal V = state->getSVal(E); + SVal V = state->getSVal(msg.getOriginExpr()); addSelfFlag(V, SelfFlag_InitRes, C); return; } @@ -301,6 +301,6 @@ static bool isInitializationMethod(const ObjCMethodDecl *MD) { /*ignorePrefix=*/false) == cocoa::InitRule; } -static bool isInitMessage(const ObjCMessageExpr *E) { - return cocoa::deriveNamingConvention(E->getSelector()) == cocoa::InitRule; +static bool isInitMessage(const ObjCMessage &msg) { + return cocoa::deriveNamingConvention(msg.getSelector()) == cocoa::InitRule; } diff --git a/lib/StaticAnalyzer/ObjCMessage.cpp b/lib/StaticAnalyzer/ObjCMessage.cpp new file mode 100644 index 0000000000..53c7175013 --- /dev/null +++ b/lib/StaticAnalyzer/ObjCMessage.cpp @@ -0,0 +1,99 @@ +//===- ObjCMessage.cpp - Wrapper for ObjC messages and dot syntax -*- C++ -*--// +// +// The LLVM Compiler Infrastruct |