DynamicTypePropagation.cpp revision 239462
1239313Sdim//== DynamicTypePropagation.cpp ----------------------------------- -*- C++ -*--=// 2239313Sdim// 3239313Sdim// The LLVM Compiler Infrastructure 4239313Sdim// 5239313Sdim// This file is distributed under the University of Illinois Open Source 6239313Sdim// License. See LICENSE.TXT for details. 7239313Sdim// 8239313Sdim//===----------------------------------------------------------------------===// 9239313Sdim// 10239313Sdim// This checker defines the rules for dynamic type gathering and propagation. 11239313Sdim// 12239313Sdim//===----------------------------------------------------------------------===// 13239313Sdim 14239313Sdim#include "ClangSACheckers.h" 15239313Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 16239313Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 17239313Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 18239313Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 19239313Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 20239313Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 21239313Sdim#include "clang/Basic/Builtins.h" 22239313Sdim 23239313Sdimusing namespace clang; 24239313Sdimusing namespace ento; 25239313Sdim 26239313Sdimnamespace { 27239313Sdimclass DynamicTypePropagation: 28239462Sdim public Checker< check::PreCall, 29239462Sdim check::PostCall, 30239313Sdim check::PostStmt<ImplicitCastExpr> > { 31239313Sdim const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, 32239313Sdim CheckerContext &C) const; 33239313Sdim 34239313Sdim /// \brief Return a better dynamic type if one can be derived from the cast. 35239313Sdim const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, 36239313Sdim CheckerContext &C) const; 37239313Sdimpublic: 38239462Sdim void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 39239313Sdim void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 40239313Sdim void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const; 41239313Sdim}; 42239313Sdim} 43239313Sdim 44239462Sdimstatic void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, 45239462Sdim CheckerContext &C) { 46239462Sdim assert(Region); 47239462Sdim assert(MD); 48239462Sdim 49239462Sdim ASTContext &Ctx = C.getASTContext(); 50239462Sdim QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent())); 51239462Sdim 52239462Sdim ProgramStateRef State = C.getState(); 53239462Sdim State = State->setDynamicTypeInfo(Region, Ty, /*CanBeSubclass=*/false); 54239462Sdim C.addTransition(State); 55239462Sdim return; 56239462Sdim} 57239462Sdim 58239462Sdimvoid DynamicTypePropagation::checkPreCall(const CallEvent &Call, 59239462Sdim CheckerContext &C) const { 60239462Sdim if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { 61239462Sdim // C++11 [class.cdtor]p4: When a virtual function is called directly or 62239462Sdim // indirectly from a constructor or from a destructor, including during 63239462Sdim // the construction or destruction of the class���s non-static data members, 64239462Sdim // and the object to which the call applies is the object under 65239462Sdim // construction or destruction, the function called is the final overrider 66239462Sdim // in the constructor's or destructor's class and not one overriding it in 67239462Sdim // a more-derived class. 68239462Sdim 69239462Sdim switch (Ctor->getOriginExpr()->getConstructionKind()) { 70239462Sdim case CXXConstructExpr::CK_Complete: 71239462Sdim case CXXConstructExpr::CK_Delegating: 72239462Sdim // No additional type info necessary. 73239462Sdim return; 74239462Sdim case CXXConstructExpr::CK_NonVirtualBase: 75239462Sdim case CXXConstructExpr::CK_VirtualBase: 76239462Sdim if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) 77239462Sdim recordFixedType(Target, Ctor->getDecl(), C); 78239462Sdim return; 79239462Sdim } 80239462Sdim 81239462Sdim return; 82239462Sdim } 83239462Sdim 84239462Sdim if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { 85239462Sdim // C++11 [class.cdtor]p4 (see above) 86239462Sdim 87239462Sdim const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion(); 88239462Sdim if (!Target) 89239462Sdim return; 90239462Sdim 91239462Sdim // FIXME: getRuntimeDefinition() can be expensive. It would be better to do 92239462Sdim // this when we are entering the stack frame for the destructor. 93239462Sdim const Decl *D = Dtor->getRuntimeDefinition().getDecl(); 94239462Sdim if (!D) 95239462Sdim return; 96239462Sdim 97239462Sdim recordFixedType(Target, cast<CXXDestructorDecl>(D), C); 98239462Sdim return; 99239462Sdim } 100239462Sdim} 101239462Sdim 102239313Sdimvoid DynamicTypePropagation::checkPostCall(const CallEvent &Call, 103239313Sdim CheckerContext &C) const { 104239313Sdim // We can obtain perfect type info for return values from some calls. 105239313Sdim if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) { 106239313Sdim 107239313Sdim // Get the returned value if it's a region. 108239313Sdim SVal Result = C.getSVal(Call.getOriginExpr()); 109239313Sdim const MemRegion *RetReg = Result.getAsRegion(); 110239313Sdim if (!RetReg) 111239313Sdim return; 112239313Sdim 113239313Sdim ProgramStateRef State = C.getState(); 114239313Sdim 115239313Sdim switch (Msg->getMethodFamily()) { 116239313Sdim default: 117239313Sdim break; 118239313Sdim 119239313Sdim // We assume that the type of the object returned by alloc and new are the 120239313Sdim // pointer to the object of the class specified in the receiver of the 121239313Sdim // message. 122239313Sdim case OMF_alloc: 123239313Sdim case OMF_new: { 124239313Sdim // Get the type of object that will get created. 125239313Sdim const ObjCMessageExpr *MsgE = Msg->getOriginExpr(); 126239313Sdim const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C); 127239313Sdim if (!ObjTy) 128239313Sdim return; 129239313Sdim QualType DynResTy = 130239313Sdim C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0)); 131239313Sdim C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false)); 132239313Sdim break; 133239313Sdim } 134239313Sdim case OMF_init: { 135239313Sdim // Assume, the result of the init method has the same dynamic type as 136239313Sdim // the receiver and propagate the dynamic type info. 137239313Sdim const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); 138239313Sdim if (!RecReg) 139239313Sdim return; 140239313Sdim DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg); 141239313Sdim C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType)); 142239313Sdim break; 143239313Sdim } 144239313Sdim } 145239462Sdim 146239462Sdim return; 147239313Sdim } 148239462Sdim 149239462Sdim if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { 150239462Sdim // We may need to undo the effects of our pre-call check. 151239462Sdim switch (Ctor->getOriginExpr()->getConstructionKind()) { 152239462Sdim case CXXConstructExpr::CK_Complete: 153239462Sdim case CXXConstructExpr::CK_Delegating: 154239462Sdim // No additional work necessary. 155239462Sdim // Note: This will leave behind the actual type of the object for 156239462Sdim // complete constructors, but arguably that's a good thing, since it 157239462Sdim // means the dynamic type info will be correct even for objects 158239462Sdim // constructed with operator new. 159239462Sdim return; 160239462Sdim case CXXConstructExpr::CK_NonVirtualBase: 161239462Sdim case CXXConstructExpr::CK_VirtualBase: 162239462Sdim if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) { 163239462Sdim // We just finished a base constructor. Now we can use the subclass's 164239462Sdim // type when resolving virtual calls. 165239462Sdim const Decl *D = C.getLocationContext()->getDecl(); 166239462Sdim recordFixedType(Target, cast<CXXConstructorDecl>(D), C); 167239462Sdim } 168239462Sdim return; 169239462Sdim } 170239462Sdim } 171239313Sdim} 172239313Sdim 173239313Sdimvoid DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE, 174239313Sdim CheckerContext &C) const { 175239313Sdim // We only track dynamic type info for regions. 176239313Sdim const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); 177239313Sdim if (!ToR) 178239313Sdim return; 179239313Sdim 180239313Sdim switch (CastE->getCastKind()) { 181239313Sdim default: 182239313Sdim break; 183239313Sdim case CK_BitCast: 184239313Sdim // Only handle ObjCObjects for now. 185239313Sdim if (const Type *NewTy = getBetterObjCType(CastE, C)) 186239313Sdim C.addTransition(C.getState()->setDynamicTypeInfo(ToR, QualType(NewTy,0))); 187239313Sdim break; 188239313Sdim } 189239313Sdim return; 190239313Sdim} 191239313Sdim 192239313Sdimconst ObjCObjectType * 193239313SdimDynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, 194239313Sdim CheckerContext &C) const { 195239313Sdim if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) { 196239313Sdim if (const ObjCObjectType *ObjTy 197239313Sdim = MsgE->getClassReceiver()->getAs<ObjCObjectType>()) 198239313Sdim return ObjTy; 199239313Sdim } 200239313Sdim 201239313Sdim if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) { 202239313Sdim if (const ObjCObjectType *ObjTy 203239313Sdim = MsgE->getSuperType()->getAs<ObjCObjectType>()) 204239313Sdim return ObjTy; 205239313Sdim } 206239313Sdim 207239313Sdim const Expr *RecE = MsgE->getInstanceReceiver(); 208239313Sdim if (!RecE) 209239313Sdim return 0; 210239313Sdim 211239313Sdim RecE= RecE->IgnoreParenImpCasts(); 212239313Sdim if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) { 213239313Sdim const StackFrameContext *SFCtx = C.getStackFrame(); 214239313Sdim // Are we calling [self alloc]? If this is self, get the type of the 215239313Sdim // enclosing ObjC class. 216239313Sdim if (DRE->getDecl() == SFCtx->getSelfDecl()) { 217239313Sdim if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl())) 218239313Sdim if (const ObjCObjectType *ObjTy = 219239313Sdim dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl())) 220239313Sdim return ObjTy; 221239313Sdim } 222239313Sdim } 223239313Sdim return 0; 224239313Sdim} 225239313Sdim 226239313Sdim// Return a better dynamic type if one can be derived from the cast. 227239313Sdim// Compare the current dynamic type of the region and the new type to which we 228239313Sdim// are casting. If the new type is lower in the inheritance hierarchy, pick it. 229239313Sdimconst ObjCObjectPointerType * 230239313SdimDynamicTypePropagation::getBetterObjCType(const Expr *CastE, 231239313Sdim CheckerContext &C) const { 232239313Sdim const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); 233239313Sdim assert(ToR); 234239313Sdim 235239313Sdim // Get the old and new types. 236239313Sdim const ObjCObjectPointerType *NewTy = 237239313Sdim CastE->getType()->getAs<ObjCObjectPointerType>(); 238239313Sdim if (!NewTy) 239239313Sdim return 0; 240239313Sdim QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType(); 241239313Sdim if (OldDTy.isNull()) { 242239313Sdim return NewTy; 243239313Sdim } 244239313Sdim const ObjCObjectPointerType *OldTy = 245239313Sdim OldDTy->getAs<ObjCObjectPointerType>(); 246239313Sdim if (!OldTy) 247239313Sdim return 0; 248239313Sdim 249239313Sdim // Id the old type is 'id', the new one is more precise. 250239313Sdim if (OldTy->isObjCIdType() && !NewTy->isObjCIdType()) 251239313Sdim return NewTy; 252239313Sdim 253239313Sdim // Return new if it's a subclass of old. 254239313Sdim const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl(); 255239313Sdim const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl(); 256239313Sdim if (ToI && FromI && FromI->isSuperClassOf(ToI)) 257239313Sdim return NewTy; 258239313Sdim 259239313Sdim return 0; 260239313Sdim} 261239313Sdim 262239313Sdimvoid ento::registerDynamicTypePropagation(CheckerManager &mgr) { 263239313Sdim mgr.registerChecker<DynamicTypePropagation>(); 264239313Sdim} 265