DynamicTypePropagation.cpp revision c4c647c88ced2e953f15f8987952ede9b96aa969
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->addDynamicTypeInfo(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      assert(RecReg);
79      DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg);
80      C.addTransition(State->addDynamicTypeInfo(RetReg, RecDynType));
81      break;
82    }
83    }
84  }
85}
86
87void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE,
88                                           CheckerContext &C) const {
89  // We only track dynamic type info for regions.
90  const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
91  if (!ToR)
92    return;
93
94  switch (CastE->getCastKind()) {
95  default:
96    break;
97  case CK_BitCast:
98    // Only handle ObjCObjects for now.
99    if (const Type *NewTy = getBetterObjCType(CastE, C))
100      C.addTransition(C.getState()->addDynamicTypeInfo(ToR, QualType(NewTy,0)));
101    break;
102  }
103  return;
104}
105
106const ObjCObjectType *
107DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
108                                                    CheckerContext &C) const {
109  if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) {
110    if (const ObjCObjectType *ObjTy
111          = MsgE->getClassReceiver()->getAs<ObjCObjectType>())
112    return ObjTy;
113  }
114
115  if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) {
116    if (const ObjCObjectType *ObjTy
117          = MsgE->getSuperType()->getAs<ObjCObjectType>())
118      return ObjTy;
119  }
120
121  const Expr *RecE = MsgE->getInstanceReceiver();
122  if (!RecE)
123    return 0;
124
125  RecE= RecE->IgnoreParenImpCasts();
126  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) {
127    const StackFrameContext *SFCtx = C.getCurrentStackFrame();
128    // Are we calling [self alloc]? If this is self, get the type of the
129    // enclosing ObjC class.
130    if (DRE->getDecl() == SFCtx->getSelfDecl()) {
131      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl()))
132        if (const ObjCObjectType *ObjTy =
133            dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl()))
134          return ObjTy;
135    }
136  }
137  return 0;
138}
139
140// Return a better dynamic type if one can be derived from the cast.
141// Compare the current dynamic type of the region and the new type to which we
142// are casting. If the new type is lower in the inheritance hierarchy, pick it.
143const ObjCObjectPointerType *
144DynamicTypePropagation::getBetterObjCType(const Expr *CastE,
145                                          CheckerContext &C) const {
146  const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
147  assert(ToR);
148
149  // Get the old and new types.
150  const ObjCObjectPointerType *NewTy =
151      CastE->getType()->getAs<ObjCObjectPointerType>();
152  if (!NewTy)
153    return 0;
154  QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType();
155  if (OldDTy.isNull()) {
156    return NewTy;
157  }
158  const ObjCObjectPointerType *OldTy =
159    OldDTy->getAs<ObjCObjectPointerType>();
160  if (!OldTy)
161    return 0;
162
163  // Id the old type is 'id', the new one is more precise.
164  if (OldTy->isObjCIdType() && !NewTy->isObjCIdType())
165    return NewTy;
166
167  // Return new if it's a subclass of old.
168  const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl();
169  const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl();
170  if (ToI && FromI && FromI->isSuperClassOf(ToI))
171    return NewTy;
172
173  return 0;
174}
175
176void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
177  mgr.registerChecker<DynamicTypePropagation>();
178}
179