//===- 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/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprCXX.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Compiler.h" namespace clang { namespace ento { using llvm::StrInStrNoCase; /// \brief Represents both explicit ObjC message expressions and implicit /// messages that are sent for handling properties in dot syntax. class ObjCMessage { const ObjCMessageExpr *Msg; const ObjCPropertyRefExpr *PE; const bool IsPropSetter; public: ObjCMessage() : Msg(0), PE(0), IsPropSetter(false) {} ObjCMessage(const ObjCMessageExpr *E, const ObjCPropertyRefExpr *pe = 0, bool isSetter = false) : Msg(E), PE(pe), IsPropSetter(isSetter) { assert(E && "should not be initialized with null expression"); } bool isValid() const { return Msg; } bool isPureMessageExpr() const { return !PE; } bool isPropertyGetter() const { return PE && !IsPropSetter; } bool isPropertySetter() const { return IsPropSetter; } const Expr *getMessageExpr() const { return Msg; } QualType getType(ASTContext &ctx) const { return Msg->getType(); } QualType getResultType(ASTContext &ctx) const { if (const ObjCMethodDecl *MD = Msg->getMethodDecl()) return MD->getResultType(); return getType(ctx); } ObjCMethodFamily getMethodFamily() const { return Msg->getMethodFamily(); } Selector getSelector() const { return Msg->getSelector(); } const Expr *getInstanceReceiver() const { return Msg->getInstanceReceiver(); } SVal getInstanceReceiverSVal(ProgramStateRef State, const LocationContext *LC) const { if (!isInstanceMessage()) return UndefinedVal(); if (const Expr *Ex = getInstanceReceiver()) return State->getSValAsScalarOrLoc(Ex, LC); // An instance message with no expression means we are sending to super. // In this case the object reference is the same as 'self'. const ImplicitParamDecl *SelfDecl = LC->getSelfDecl(); assert(SelfDecl && "No message receiver Expr, but not in an ObjC method"); return State->getSVal(State->getRegion(SelfDecl, LC)); } bool isInstanceMessage() const { return Msg->isInstanceMessage(); } const ObjCMethodDecl *getMethodDecl() const { return Msg->getMethodDecl(); } const ObjCInterfaceDecl *getReceiverInterface() const { return Msg->getReceiverInterface(); } SourceLocation getSuperLoc() const { if (PE) return PE->getReceiverLocation(); return Msg->getSuperLoc(); } SourceRange getSourceRange() const LLVM_READONLY { if (PE) return PE->getSourceRange(); return Msg->getSourceRange(); } unsigned getNumArgs() const { return Msg->getNumArgs(); } SVal getArgSVal(unsigned i, const LocationContext *LCtx, ProgramStateRef state) const { assert(i < getNumArgs() && "Invalid index for argument"); return state->getSVal(Msg->getArg(i), LCtx); } QualType getArgType(unsigned i) const { assert(i < getNumArgs() && "Invalid index for argument"); return Msg->getArg(i)->getType(); } const Expr *getArgExpr(unsigned i) const { assert(i < getNumArgs() && "Invalid index for argument"); return Msg->getArg(i); } SourceRange getArgSourceRange(unsigned i) const { const Expr *argE = getArgExpr(i); return argE->getSourceRange(); } SourceRange getReceiverSourceRange() const { if (PE) { if (PE->isObjectReceiver()) return PE->getBase()->getSourceRange(); } else { return Msg->getReceiverRange(); } // FIXME: This isn't a range. return PE->getReceiverLocation(); } }; /// \brief Common wrapper for a call expression, ObjC message, or C++ /// constructor, mainly to provide a common interface for their arguments. class CallOrObjCMessage { llvm::PointerUnion CallE; ObjCMessage Msg; ProgramStateRef State; const LocationContext *LCtx; public: CallOrObjCMessage(const CallExpr *callE, ProgramStateRef state, const LocationContext *lctx) : CallE(callE), State(state), LCtx(lctx) {} CallOrObjCMessage(const CXXConstructExpr *consE, ProgramStateRef state, const LocationContext *lctx) : CallE(consE), State(state), LCtx(lctx) {} CallOrObjCMessage(const ObjCMessage &msg, ProgramStateRef state, const LocationContext *lctx) : CallE((CallExpr *)0), Msg(msg), State(state), LCtx(lctx) {} QualType getResultType(ASTContext &ctx) const; bool isFunctionCall() const { return CallE && CallE.is(); } bool isCXXConstructExpr() const { return CallE && CallE.is(); } bool isObjCMessage() const { return !CallE; } bool isCXXCall() const { const CallExpr *ActualCallE = CallE.dyn_cast(); return ActualCallE && isa(ActualCallE); } /// Check if the callee is declared in the system header. bool isInSystemHeader() const { if (const Decl *FD = getDecl()) { const SourceManager &SM = State->getStateManager().getContext().getSourceManager(); return SM.isInSystemHeader(FD->getLocation()); } return false; } const Expr *getOriginExpr() const { if (!CallE) return Msg.getMessageExpr(); if (const CXXConstructExpr *Ctor = CallE.dyn_cast()) return Ctor; return CallE.get(); } SVal getFunctionCallee() const; SVal getCXXCallee() const; SVal getInstanceMessageReceiver(const LocationContext *LC) const; /// Get the declaration of the function or method. const Decl *getDecl() const; unsigned getNumArgs() const { if (!CallE) return Msg.getNumArgs(); if (const CXXConstructExpr *Ctor = CallE.dyn_cast()) return Ctor->getNumArgs(); return CallE.get()->getNumArgs(); } SVal getArgSVal(unsigned i) const { assert(i < getNumArgs()); if (!CallE) return Msg.getArgSVal(i, LCtx, State); return State->getSVal(getArg(i), LCtx); } const Expr *getArg(unsigned i) const { assert(i < getNumArgs()); if (!CallE) return Msg.getArgExpr(i); if (const CXXConstructExpr *Ctor = CallE.dyn_cast()) return Ctor->getArg(i); return CallE.get()->getArg(i); } SourceRange getArgSourceRange(unsigned i) const { assert(i < getNumArgs()); if (CallE) return getArg(i)->getSourceRange(); return Msg.getArgSourceRange(i); } SourceRange getReceiverSourceRange() const { assert(isObjCMessage()); return Msg.getReceiverSourceRange(); } /// \brief Check if the name corresponds to a CoreFoundation or CoreGraphics /// function that allows objects to escape. /// /// Many methods allow a tracked object to escape. For example: /// /// CFMutableDictionaryRef x = CFDictionaryCreateMutable(..., customDeallocator); /// CFDictionaryAddValue(y, key, x); /// /// We handle this and similar cases with the following heuristic. If the /// function name contains "InsertValue", "SetValue", "AddValue", /// "AppendValue", or "SetAttribute", then we assume that arguments may /// escape. // // TODO: To reduce false negatives here, we should track the container // allocation site and check if a proper deallocator was set there. static bool isCFCGAllowingEscape(StringRef FName) { if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G')) if (StrInStrNoCase(FName, "InsertValue") != StringRef::npos|| StrInStrNoCase(FName, "AddValue") != StringRef::npos || StrInStrNoCase(FName, "SetValue") != StringRef::npos || StrInStrNoCase(FName, "WithData") != StringRef::npos || StrInStrNoCase(FName, "AppendValue") != StringRef::npos|| StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) { return true; } return false; } }; } } #endif