DynamicTypePropagation.cpp revision d4fe57f7f7a8793227effc1274d70ec44cee9a4f
1//== DynamicTypePropagation.cpp ----------------------------------- -*- C++ -*--=//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This checker defines the rules for dynamic type gathering and propagation.
11//
12//===----------------------------------------------------------------------===//
13
14#include "ClangSACheckers.h"
15#include "clang/StaticAnalyzer/Core/Checker.h"
16#include "clang/StaticAnalyzer/Core/CheckerManager.h"
17#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
20#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21#include "clang/Basic/Builtins.h"
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27class DynamicTypePropagation:
28    public Checker< check::PostCall,
29                    check::PostStmt<ImplicitCastExpr> > {
30  const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
31                                                    CheckerContext &C) const;
32
33  /// \brief Return a better dynamic type if one can be derived from the cast.
34  const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE,
35                                                 CheckerContext &C) const;
36public:
37  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
38  void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const;
39};
40}
41
42void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
43                                           CheckerContext &C) const {
44  // We can obtain perfect type info for return values from some calls.
45  if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
46
47    // Get the returned value if it's a region.
48    SVal Result = C.getSVal(Call.getOriginExpr());
49    const MemRegion *RetReg = Result.getAsRegion();
50    if (!RetReg)
51      return;
52
53    ProgramStateRef State = C.getState();
54
55    switch (Msg->getMethodFamily()) {
56    default:
57      break;
58
59    // We assume that the type of the object returned by alloc and new are the
60    // pointer to the object of the class specified in the receiver of the
61    // message.
62    case OMF_alloc:
63    case OMF_new: {
64      // Get the type of object that will get created.
65      const ObjCMessageExpr *MsgE = Msg->getOriginExpr();
66      const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C);
67      if (!ObjTy)
68        return;
69      QualType DynResTy =
70                 C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0));
71      C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy));
72      break;
73    }
74    case OMF_init: {
75      // Assume, the result of the init method has the same dynamic type as
76      // the receiver and propagate the dynamic type info.
77      const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion();
78      if (!RecReg)
79        return;
80      DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg);
81      C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType));
82      break;
83    }
84    }
85  }
86}
87
88void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE,
89                                           CheckerContext &C) const {
90  // We only track dynamic type info for regions.
91  const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
92  if (!ToR)
93    return;
94
95  switch (CastE->getCastKind()) {
96  default:
97    break;
98  case CK_BitCast:
99    // Only handle ObjCObjects for now.
100    if (const Type *NewTy = getBetterObjCType(CastE, C))
101      C.addTransition(C.getState()->setDynamicTypeInfo(ToR, QualType(NewTy,0)));
102    break;
103  }
104  return;
105}
106
107const ObjCObjectType *
108DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
109                                                    CheckerContext &C) const {
110  if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) {
111    if (const ObjCObjectType *ObjTy
112          = MsgE->getClassReceiver()->getAs<ObjCObjectType>())
113    return ObjTy;
114  }
115
116  if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) {
117    if (const ObjCObjectType *ObjTy
118          = MsgE->getSuperType()->getAs<ObjCObjectType>())
119      return ObjTy;
120  }
121
122  const Expr *RecE = MsgE->getInstanceReceiver();
123  if (!RecE)
124    return 0;
125
126  RecE= RecE->IgnoreParenImpCasts();
127  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) {
128    const StackFrameContext *SFCtx = C.getCurrentStackFrame();
129    // Are we calling [self alloc]? If this is self, get the type of the
130    // enclosing ObjC class.
131    if (DRE->getDecl() == SFCtx->getSelfDecl()) {
132      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl()))
133        if (const ObjCObjectType *ObjTy =
134            dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl()))
135          return ObjTy;
136    }
137  }
138  return 0;
139}
140
141// Return a better dynamic type if one can be derived from the cast.
142// Compare the current dynamic type of the region and the new type to which we
143// are casting. If the new type is lower in the inheritance hierarchy, pick it.
144const ObjCObjectPointerType *
145DynamicTypePropagation::getBetterObjCType(const Expr *CastE,
146                                          CheckerContext &C) const {
147  const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
148  assert(ToR);
149
150  // Get the old and new types.
151  const ObjCObjectPointerType *NewTy =
152      CastE->getType()->getAs<ObjCObjectPointerType>();
153  if (!NewTy)
154    return 0;
155  QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType();
156  if (OldDTy.isNull()) {
157    return NewTy;
158  }
159  const ObjCObjectPointerType *OldTy =
160    OldDTy->getAs<ObjCObjectPointerType>();
161  if (!OldTy)
162    return 0;
163
164  // Id the old type is 'id', the new one is more precise.
165  if (OldTy->isObjCIdType() && !NewTy->isObjCIdType())
166    return NewTy;
167
168  // Return new if it's a subclass of old.
169  const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl();
170  const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl();
171  if (ToI && FromI && FromI->isSuperClassOf(ToI))
172    return NewTy;
173
174  return 0;
175}
176
177void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
178  mgr.registerChecker<DynamicTypePropagation>();
179}
180