DynamicTypePropagation.cpp [plain text]
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/Basic/Builtins.h"
using namespace clang;
using namespace ento;
namespace {
class DynamicTypePropagation:
public Checker< check::PreCall,
check::PostCall,
check::PostStmt<ImplicitCastExpr> > {
const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
CheckerContext &C) const;
const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE,
CheckerContext &C) const;
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const;
};
}
static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD,
CheckerContext &C) {
assert(Region);
assert(MD);
ASTContext &Ctx = C.getASTContext();
QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent()));
ProgramStateRef State = C.getState();
State = State->setDynamicTypeInfo(Region, Ty, false);
C.addTransition(State);
return;
}
void DynamicTypePropagation::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
switch (Ctor->getOriginExpr()->getConstructionKind()) {
case CXXConstructExpr::CK_Complete:
case CXXConstructExpr::CK_Delegating:
return;
case CXXConstructExpr::CK_NonVirtualBase:
case CXXConstructExpr::CK_VirtualBase:
if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion())
recordFixedType(Target, Ctor->getDecl(), C);
return;
}
return;
}
if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
if (!Dtor->isBaseDestructor())
return;
const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion();
if (!Target)
return;
const Decl *D = Dtor->getDecl();
if (!D)
return;
recordFixedType(Target, cast<CXXDestructorDecl>(D), C);
return;
}
}
void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
SVal Result = C.getSVal(Call.getOriginExpr());
const MemRegion *RetReg = Result.getAsRegion();
if (!RetReg)
return;
ProgramStateRef State = C.getState();
switch (Msg->getMethodFamily()) {
default:
break;
case OMF_alloc:
case OMF_new: {
const ObjCMessageExpr *MsgE = Msg->getOriginExpr();
const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C);
if (!ObjTy)
return;
QualType DynResTy =
C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0));
C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false));
break;
}
case OMF_init: {
const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion();
if (!RecReg)
return;
DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg);
C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType));
break;
}
}
return;
}
if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
switch (Ctor->getOriginExpr()->getConstructionKind()) {
case CXXConstructExpr::CK_Complete:
case CXXConstructExpr::CK_Delegating:
return;
case CXXConstructExpr::CK_NonVirtualBase:
case CXXConstructExpr::CK_VirtualBase:
if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) {
const Decl *D = C.getLocationContext()->getDecl();
recordFixedType(Target, cast<CXXConstructorDecl>(D), C);
}
return;
}
}
}
void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE,
CheckerContext &C) const {
const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
if (!ToR)
return;
switch (CastE->getCastKind()) {
default:
break;
case CK_BitCast:
if (const Type *NewTy = getBetterObjCType(CastE, C))
C.addTransition(C.getState()->setDynamicTypeInfo(ToR, QualType(NewTy,0)));
break;
}
return;
}
const ObjCObjectType *
DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
CheckerContext &C) const {
if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) {
if (const ObjCObjectType *ObjTy
= MsgE->getClassReceiver()->getAs<ObjCObjectType>())
return ObjTy;
}
if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) {
if (const ObjCObjectType *ObjTy
= MsgE->getSuperType()->getAs<ObjCObjectType>())
return ObjTy;
}
const Expr *RecE = MsgE->getInstanceReceiver();
if (!RecE)
return 0;
RecE= RecE->IgnoreParenImpCasts();
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) {
const StackFrameContext *SFCtx = C.getStackFrame();
if (DRE->getDecl() == SFCtx->getSelfDecl()) {
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl()))
if (const ObjCObjectType *ObjTy =
dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl()))
return ObjTy;
}
}
return 0;
}
const ObjCObjectPointerType *
DynamicTypePropagation::getBetterObjCType(const Expr *CastE,
CheckerContext &C) const {
const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
assert(ToR);
const ObjCObjectPointerType *NewTy =
CastE->getType()->getAs<ObjCObjectPointerType>();
if (!NewTy)
return 0;
QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType();
if (OldDTy.isNull()) {
return NewTy;
}
const ObjCObjectPointerType *OldTy =
OldDTy->getAs<ObjCObjectPointerType>();
if (!OldTy)
return 0;
if (OldTy->isObjCIdType() && !NewTy->isObjCIdType())
return NewTy;
const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl();
const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl();
if (ToI && FromI && FromI->isSuperClassOf(ToI))
return NewTy;
return 0;
}
void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
mgr.registerChecker<DynamicTypePropagation>();
}