blob: f01bad02c2033be0d735744e9feaecac0cb3cc63 [file] [log] [blame]
//===- 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<const CallExpr *, const CXXConstructExpr *> CallE;
ObjCMessage Msg;
ProgramStateRef State;
const LocationContext *LCtx;
bool isCallbackArg(unsigned Idx, const Type *T) const;
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<const CallExpr *>();
}
bool isCXXConstructExpr() const {
return CallE && CallE.is<const CXXConstructExpr *>();
}
bool isObjCMessage() const {
return !CallE;
}
bool isCXXCall() const {
const CallExpr *ActualCallE = CallE.dyn_cast<const CallExpr *>();
return ActualCallE && isa<CXXMemberCallExpr>(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<const CXXConstructExpr *>())
return Ctor;
return CallE.get<const CallExpr *>();
}
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<const CXXConstructExpr *>())
return Ctor->getNumArgs();
return CallE.get<const CallExpr *>()->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<const CXXConstructExpr *>())
return Ctor->getArg(i);
return CallE.get<const CallExpr *>()->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 one of the arguments might be a callback.
bool hasNonZeroCallbackArg() const;
/// \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);
// Check if this kind of expression can be inlined by the analyzer.
static bool canBeInlined(const Stmt *S) {
return isa<CallExpr>(S);
}
};
}
}
#endif