_PostVisit(C, S);
}
+ void GR_visitObjCMessage(ExplodedNodeSet &Dst,
+ StmtNodeBuilder &Builder,
+ ExprEngine &Eng,
+ const ObjCMessage &msg,
+ ExplodedNode *Pred, void *tag, bool isPrevisit) {
+ CheckerContext C(Dst, Builder, Eng, Pred, tag,
+ isPrevisit ? ProgramPoint::PreStmtKind :
+ ProgramPoint::PostStmtKind, 0, msg.getOriginExpr());
+ if (isPrevisit)
+ preVisitObjCMessage(C, msg);
+ else
+ postVisitObjCMessage(C, msg);
+ }
+
bool GR_evalNilReceiver(ExplodedNodeSet &Dst, StmtNodeBuilder &Builder,
- ExprEngine &Eng, const ObjCMessageExpr *ME,
+ ExprEngine &Eng, const ObjCMessage &msg,
ExplodedNode *Pred, const GRState *state, void *tag) {
CheckerContext C(Dst, Builder, Eng, Pred, tag, ProgramPoint::PostStmtKind,
- 0, ME, state);
- return evalNilReceiver(C, ME);
+ 0, msg.getOriginExpr(), state);
+ return evalNilReceiver(C, msg);
}
bool GR_evalCallExpr(ExplodedNodeSet &Dst, StmtNodeBuilder &Builder,
virtual ~Checker();
virtual void _PreVisit(CheckerContext &C, const Stmt *S) {}
virtual void _PostVisit(CheckerContext &C, const Stmt *S) {}
+ virtual void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg) {}
+ virtual void postVisitObjCMessage(CheckerContext &C, ObjCMessage msg) {}
virtual void visitLocation(CheckerContext &C, const Stmt *S, SVal location,
bool isLoad) {}
virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE,
ExprEngine &Eng,
const Stmt *Condition, void *tag) {}
- virtual bool evalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME) {
+ virtual bool evalNilReceiver(CheckerContext &C, ObjCMessage msg) {
return false;
}
PREVISIT(DeclStmt, Stmt)
PREVISIT(ImplicitCastExpr, CastExpr)
PREVISIT(ObjCAtSynchronizedStmt, Stmt)
-PREVISIT(ObjCMessageExpr, Stmt)
PREVISIT(ReturnStmt, Stmt)
POSTVISIT(BlockExpr, Stmt)
POSTVISIT(CompoundAssignOperator, BinaryOperator)
POSTVISIT(CXXOperatorCallExpr, GenericCall)
POSTVISIT(CXXMemberCallExpr, GenericCall)
-POSTVISIT(ObjCMessageExpr, Stmt)
POSTVISIT(ObjCIvarRefExpr, Stmt)
#undef PREVISIT
void CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
CallbackKind Kind);
+ void CheckerVisitObjCMessage(const ObjCMessage &msg, ExplodedNodeSet &Dst,
+ ExplodedNodeSet &Src, bool isPrevisit);
+
bool CheckerEvalCall(const CallExpr *CE,
ExplodedNodeSet &Dst,
ExplodedNode *Pred);
- void CheckerEvalNilReceiver(const ObjCMessageExpr *ME,
+ void CheckerEvalNilReceiver(const ObjCMessage &msg,
ExplodedNodeSet &Dst,
const GRState *state,
ExplodedNode *Pred);
}
protected:
- void evalObjCMessageExpr(ExplodedNodeSet& Dst, const ObjCMessageExpr* ME,
- ExplodedNode* Pred, const GRState *state) {
+ void evalObjCMessage(ExplodedNodeSet& Dst, const ObjCMessage &msg,
+ ExplodedNode* Pred, const GRState *state) {
assert (Builder && "StmtNodeBuilder must be defined.");
- getTF().evalObjCMessageExpr(Dst, *this, *Builder, ME, Pred, state);
+ getTF().evalObjCMessage(Dst, *this, *Builder, msg, Pred, state);
}
const GRState* MarkBranch(const GRState* St, const Stmt* Terminator,
--- /dev/null
+//===- ObjCMessage.h - Wrapper for ObjC messages and dot syntax ---*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ObjCMessage which serves as a common wrapper for ObjC
+// message expressions or implicit messages for loading/storing ObjC properties.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_OBJCMESSAGE
+#define LLVM_CLANG_STATICANALYZER_PATHSENSITIVE_OBJCMESSAGE
+
+#include "clang/StaticAnalyzer/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/PathSensitive/GRState.h"
+#include "clang/AST/ExprObjC.h"
+
+namespace clang {
+namespace ento {
+
+/// \brief Represents both explicit ObjC message expressions and implicit
+/// messages that are sent for handling properties in dot syntax.
+class ObjCMessage {
+ const Expr *MsgOrPropE;
+ const Expr *OriginE;
+ bool IsPropSetter;
+ SVal SetterArgV;
+
+protected:
+ ObjCMessage(const Expr *E, const Expr *origE, bool isSetter, SVal setArgV)
+ : MsgOrPropE(E), OriginE(origE),
+ IsPropSetter(isSetter), SetterArgV(setArgV) { }
+
+public:
+ ObjCMessage() : MsgOrPropE(0), OriginE(0) { }
+
+ ObjCMessage(const ObjCMessageExpr *E)
+ : MsgOrPropE(E), OriginE(E) {
+ assert(E && "should not be initialized with null expression");
+ }
+
+ bool isValid() const { return MsgOrPropE != 0; }
+ bool isInvalid() const { return !isValid(); }
+
+ bool isMessageExpr() const {
+ return isValid() && isa<ObjCMessageExpr>(MsgOrPropE);
+ }
+
+ bool isPropertyGetter() const {
+ return isValid() &&
+ isa<ObjCPropertyRefExpr>(MsgOrPropE) && !IsPropSetter;
+ }
+
+ bool isPropertySetter() const {
+ return isValid() &&
+ isa<ObjCPropertyRefExpr>(MsgOrPropE) && IsPropSetter;
+ }
+
+ const Expr *getOriginExpr() const { return OriginE; }
+
+ QualType getType(ASTContext &ctx) const;
+
+ QualType getResultType(ASTContext &ctx) const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ if (const ObjCMethodDecl *MD = msgE->getMethodDecl())
+ return MD->getResultType();
+ return getType(ctx);
+ }
+
+ Selector getSelector() const;
+
+ const Expr *getInstanceReceiver() const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->getInstanceReceiver();
+ return cast<ObjCPropertyRefExpr>(MsgOrPropE)->getBase();
+ }
+
+ bool isInstanceMessage() const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->isInstanceMessage();
+ const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
+ // FIXME: 'super' may be super class.
+ return propE->isObjectReceiver() || propE->isSuperReceiver();
+ }
+
+ const ObjCMethodDecl *getMethodDecl() const;
+
+ const ObjCInterfaceDecl *getReceiverInterface() const;
+
+ SourceLocation getSuperLoc() const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->getSuperLoc();
+ return cast<ObjCPropertyRefExpr>(MsgOrPropE)->getReceiverLocation();
+ }
+
+ SourceRange getSourceRange() const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ return MsgOrPropE->getSourceRange();
+ }
+
+ unsigned getNumArgs() const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->getNumArgs();
+ return isPropertySetter() ? 1 : 0;
+ }
+
+ SVal getArgSVal(unsigned i, const GRState *state) const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ assert(i < getNumArgs() && "Invalid index for argument");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return state->getSVal(msgE->getArg(i));
+ assert(isPropertySetter());
+ return SetterArgV;
+ }
+
+ QualType getArgType(unsigned i) const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ assert(i < getNumArgs() && "Invalid index for argument");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->getArg(i)->getType();
+ assert(isPropertySetter());
+ return cast<ObjCPropertyRefExpr>(MsgOrPropE)->getType();
+ }
+
+ const Expr *getArgExpr(unsigned i) const;
+
+ SourceRange getArgSourceRange(unsigned i) const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ assert(i < getNumArgs() && "Invalid index for argument");
+ if (const Expr *argE = getArgExpr(i))
+ return argE->getSourceRange();
+ return OriginE->getSourceRange();
+ }
+};
+
+class ObjCPropertyGetter : public ObjCMessage {
+public:
+ ObjCPropertyGetter(const ObjCPropertyRefExpr *propE, const Expr *originE)
+ : ObjCMessage(propE, originE, false, SVal()) {
+ assert(propE && originE &&
+ "should not be initialized with null expressions");
+ }
+};
+
+class ObjCPropertySetter : public ObjCMessage {
+public:
+ ObjCPropertySetter(const ObjCPropertyRefExpr *propE, const Expr *storeE,
+ SVal argV)
+ : ObjCMessage(propE, storeE, true, argV) {
+ assert(propE && storeE &&"should not be initialized with null expressions");
+ }
+};
+
+/// \brief Common wrapper for a call expression or an ObjC message, mainly to
+/// provide a common interface for handling their arguments.
+class CallOrObjCMessage {
+ const CallExpr *CallE;
+ ObjCMessage Msg;
+ const GRState *State;
+
+public:
+ CallOrObjCMessage(const CallExpr *callE, const GRState *state)
+ : CallE(callE), State(state) { }
+ CallOrObjCMessage(const ObjCMessage &msg, const GRState *state)
+ : CallE(0), Msg(msg), State(state) { }
+
+ QualType getResultType(ASTContext &ctx) const;
+
+ unsigned getNumArgs() const {
+ if (CallE) return CallE->getNumArgs();
+ return Msg.getNumArgs();
+ }
+
+ SVal getArgSVal(unsigned i) const {
+ assert(i < getNumArgs());
+ if (CallE) return State->getSVal(CallE->getArg(i));
+ return Msg.getArgSVal(i, State);
+ }
+
+ SVal getArgSValAsScalarOrLoc(unsigned i) const;
+
+ const Expr *getArg(unsigned i) const {
+ assert(i < getNumArgs());
+ if (CallE) return CallE->getArg(i);
+ return Msg.getArgExpr(i);
+ }
+
+ SourceRange getArgSourceRange(unsigned i) const {
+ assert(i < getNumArgs());
+ if (CallE) return CallE->getArg(i)->getSourceRange();
+ return Msg.getArgSourceRange(i);
+ }
+};
+
+}
+}
+
+#endif
#include "clang/StaticAnalyzer/PathSensitive/GRState.h"
#include "clang/StaticAnalyzer/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/PathSensitive/ObjCMessage.h"
#include <vector>
namespace clang {
const CallExpr* CE, SVal L,
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.
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();
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
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);
}
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,
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
}
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
}
} 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.
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,
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);
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;
// 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)
WhitelistedSymbols.insert(Sym);
state = Update(state, Sym, *T, Summ.getArg(idx), hasErr);
if (hasErr) {
- ErrorRange = (*I)->getSourceRange();
+ ErrorRange = callOrMsg.getArgSourceRange(idx);
ErrorSym = Sym;
break;
}
// 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();
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;
}
}
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 {
HTMLDiagnostics.cpp
ManagerRegistry.cpp
MemRegion.cpp
+ ObjCMessage.cpp
PathDiagnostic.cpp
PlistDiagnostics.cpp
RangeConstraintManager.cpp
// 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;
}
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)
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;
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);
}
}
}
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 "
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;
<< "' and not the class directly";
RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
- report->addRange(ME->getSourceRange());
+ report->addRange(msg.getSourceRange());
C.EmitReport(report);
}
}
}
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)
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;
// 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
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)
}
// 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)
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);
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;
}
// 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);
Ctx.LongLongTy == CanRetTy ||
Ctx.UnsignedLongLongTy == CanRetTy))) {
if (ExplodedNode* N = C.generateSink(state))
- emitNilReceiverBug(C, ME, N);
+ emitNilReceiverBug(C, msg, N);
return;
}
// 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;
}
// 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;
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;
// 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;
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();
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
// 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);
}
//===----------------------------------------------------------------------===//
return &x;
}
- void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
+ void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg);
};
} // end anonymous namespace
}
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;
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);
}
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 {
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);
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.
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;
}
/*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;
}
--- /dev/null
+//===- ObjCMessage.cpp - Wrapper for ObjC messages and dot syntax -*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ObjCMessage which serves as a common wrapper for ObjC
+// message expressions or implicit messages for loading/storing ObjC properties.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/PathSensitive/ObjCMessage.h"
+
+using namespace clang;
+using namespace ento;
+
+QualType ObjCMessage::getType(ASTContext &ctx) const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->getType();
+ const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
+ if (isPropertySetter())
+ return ctx.VoidTy;
+ return propE->getType();
+}
+
+Selector ObjCMessage::getSelector() const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->getSelector();
+ const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
+ if (isPropertySetter())
+ return propE->getSetterSelector();
+ return propE->getGetterSelector();
+}
+
+const ObjCMethodDecl *ObjCMessage::getMethodDecl() const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->getMethodDecl();
+ const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
+ if (propE->isImplicitProperty())
+ return isPropertySetter() ? propE->getImplicitPropertySetter()
+ : propE->getImplicitPropertyGetter();
+ return 0;
+}
+
+const ObjCInterfaceDecl *ObjCMessage::getReceiverInterface() const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->getReceiverInterface();
+ const ObjCPropertyRefExpr *propE = cast<ObjCPropertyRefExpr>(MsgOrPropE);
+ if (propE->isClassReceiver())
+ return propE->getClassReceiver();
+ QualType recT;
+ if (const Expr *recE = getInstanceReceiver())
+ recT = recE->getType();
+ else {
+ assert(propE->isSuperReceiver());
+ recT = propE->getSuperReceiverType();
+ }
+ if (const ObjCObjectPointerType *Ptr = recT->getAs<ObjCObjectPointerType>())
+ return Ptr->getInterfaceDecl();
+ return 0;
+}
+
+const Expr *ObjCMessage::getArgExpr(unsigned i) const {
+ assert(isValid() && "This ObjCMessage is uninitialized!");
+ assert(i < getNumArgs() && "Invalid index for argument");
+ if (const ObjCMessageExpr *msgE = dyn_cast<ObjCMessageExpr>(MsgOrPropE))
+ return msgE->getArg(i);
+ assert(isPropertySetter());
+ if (const BinaryOperator *bop = dyn_cast<BinaryOperator>(OriginE))
+ if (bop->isAssignmentOp())
+ return bop->getRHS();
+ return 0;
+}
+
+QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const {
+ if (CallE) {
+ const Expr *Callee = CallE->getCallee();
+ if (const FunctionDecl *FD = State->getSVal(Callee).getAsFunctionDecl())
+ return FD->getResultType();
+ return CallE->getType();
+ }
+ return Msg.getResultType(ctx);
+}
+
+SVal CallOrObjCMessage::getArgSValAsScalarOrLoc(unsigned i) const {
+ assert(i < getNumArgs());
+ if (CallE) return State->getSValAsScalarOrLoc(CallE->getArg(i));
+ QualType argT = Msg.getArgType(i);
+ if (Loc::IsLocType(argT) || argT->isIntegerType())
+ return Msg.getArgSVal(i, State);
+ return UnknownVal();
+}