DynamicTypePropagation.cpp revision 6bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89
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/Basic/Builtins.h"
16#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17#include "clang/StaticAnalyzer/Core/Checker.h"
18#include "clang/StaticAnalyzer/Core/CheckerManager.h"
19#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
22
23using namespace clang;
24using namespace ento;
25
26namespace {
27class DynamicTypePropagation:
28    public Checker< check::PreCall,
29                    check::PostCall,
30                    check::PostStmt<ImplicitCastExpr>,
31                    check::PostStmt<CXXNewExpr> > {
32  const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
33                                                    CheckerContext &C) const;
34
35  /// \brief Return a better dynamic type if one can be derived from the cast.
36  const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE,
37                                                 CheckerContext &C) const;
38public:
39  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
40  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
41  void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const;
42  void checkPostStmt(const CXXNewExpr *NewE, CheckerContext &C) const;
43};
44}
45
46static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD,
47                            CheckerContext &C) {
48  assert(Region);
49  assert(MD);
50
51  ASTContext &Ctx = C.getASTContext();
52  QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent()));
53
54  ProgramStateRef State = C.getState();
55  State = State->setDynamicTypeInfo(Region, Ty, /*CanBeSubclass=*/false);
56  C.addTransition(State);
57  return;
58}
59
60void DynamicTypePropagation::checkPreCall(const CallEvent &Call,
61                                          CheckerContext &C) const {
62  if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
63    // C++11 [class.cdtor]p4: When a virtual function is called directly or
64    //   indirectly from a constructor or from a destructor, including during
65    //   the construction or destruction of the class's non-static data members,
66    //   and the object to which the call applies is the object under
67    //   construction or destruction, the function called is the final overrider
68    //   in the constructor's or destructor's class and not one overriding it in
69    //   a more-derived class.
70
71    switch (Ctor->getOriginExpr()->getConstructionKind()) {
72    case CXXConstructExpr::CK_Complete:
73    case CXXConstructExpr::CK_Delegating:
74      // No additional type info necessary.
75      return;
76    case CXXConstructExpr::CK_NonVirtualBase:
77    case CXXConstructExpr::CK_VirtualBase:
78      if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion())
79        recordFixedType(Target, Ctor->getDecl(), C);
80      return;
81    }
82
83    return;
84  }
85
86  if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
87    // C++11 [class.cdtor]p4 (see above)
88    if (!Dtor->isBaseDestructor())
89      return;
90
91    const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion();
92    if (!Target)
93      return;
94
95    const Decl *D = Dtor->getDecl();
96    if (!D)
97      return;
98
99    recordFixedType(Target, cast<CXXDestructorDecl>(D), C);
100    return;
101  }
102}
103
104void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
105                                           CheckerContext &C) const {
106  // We can obtain perfect type info for return values from some calls.
107  if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
108
109    // Get the returned value if it's a region.
110    const MemRegion *RetReg = Call.getReturnValue().getAsRegion();
111    if (!RetReg)
112      return;
113
114    ProgramStateRef State = C.getState();
115    const ObjCMethodDecl *D = Msg->getDecl();
116
117    if (D && D->hasRelatedResultType()) {
118      switch (Msg->getMethodFamily()) {
119      default:
120        break;
121
122      // We assume that the type of the object returned by alloc and new are the
123      // pointer to the object of the class specified in the receiver of the
124      // message.
125      case OMF_alloc:
126      case OMF_new: {
127        // Get the type of object that will get created.
128        const ObjCMessageExpr *MsgE = Msg->getOriginExpr();
129        const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C);
130        if (!ObjTy)
131          return;
132        QualType DynResTy =
133                 C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0));
134        C.addTransition(State->setDynamicTypeInfo(RetReg, DynResTy, false));
135        break;
136      }
137      case OMF_init: {
138        // Assume, the result of the init method has the same dynamic type as
139        // the receiver and propagate the dynamic type info.
140        const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion();
141        if (!RecReg)
142          return;
143        DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg);
144        C.addTransition(State->setDynamicTypeInfo(RetReg, RecDynType));
145        break;
146      }
147      }
148    }
149    return;
150  }
151
152  if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
153    // We may need to undo the effects of our pre-call check.
154    switch (Ctor->getOriginExpr()->getConstructionKind()) {
155    case CXXConstructExpr::CK_Complete:
156    case CXXConstructExpr::CK_Delegating:
157      // No additional work necessary.
158      // Note: This will leave behind the actual type of the object for
159      // complete constructors, but arguably that's a good thing, since it
160      // means the dynamic type info will be correct even for objects
161      // constructed with operator new.
162      return;
163    case CXXConstructExpr::CK_NonVirtualBase:
164    case CXXConstructExpr::CK_VirtualBase:
165      if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) {
166        // We just finished a base constructor. Now we can use the subclass's
167        // type when resolving virtual calls.
168        const Decl *D = C.getLocationContext()->getDecl();
169        recordFixedType(Target, cast<CXXConstructorDecl>(D), C);
170      }
171      return;
172    }
173  }
174}
175
176void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE,
177                                           CheckerContext &C) const {
178  // We only track dynamic type info for regions.
179  const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
180  if (!ToR)
181    return;
182
183  switch (CastE->getCastKind()) {
184  default:
185    break;
186  case CK_BitCast:
187    // Only handle ObjCObjects for now.
188    if (const Type *NewTy = getBetterObjCType(CastE, C))
189      C.addTransition(C.getState()->setDynamicTypeInfo(ToR, QualType(NewTy,0)));
190    break;
191  }
192  return;
193}
194
195void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE,
196                                           CheckerContext &C) const {
197  if (NewE->isArray())
198    return;
199
200  // We only track dynamic type info for regions.
201  const MemRegion *MR = C.getSVal(NewE).getAsRegion();
202  if (!MR)
203    return;
204
205  C.addTransition(C.getState()->setDynamicTypeInfo(MR, NewE->getType(),
206                                                   /*CanBeSubclass=*/false));
207}
208
209const ObjCObjectType *
210DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
211                                                    CheckerContext &C) const {
212  if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) {
213    if (const ObjCObjectType *ObjTy
214          = MsgE->getClassReceiver()->getAs<ObjCObjectType>())
215    return ObjTy;
216  }
217
218  if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) {
219    if (const ObjCObjectType *ObjTy
220          = MsgE->getSuperType()->getAs<ObjCObjectType>())
221      return ObjTy;
222  }
223
224  const Expr *RecE = MsgE->getInstanceReceiver();
225  if (!RecE)
226    return nullptr;
227
228  RecE= RecE->IgnoreParenImpCasts();
229  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) {
230    const StackFrameContext *SFCtx = C.getStackFrame();
231    // Are we calling [self alloc]? If this is self, get the type of the
232    // enclosing ObjC class.
233    if (DRE->getDecl() == SFCtx->getSelfDecl()) {
234      if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl()))
235        if (const ObjCObjectType *ObjTy =
236            dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl()))
237          return ObjTy;
238    }
239  }
240  return nullptr;
241}
242
243// Return a better dynamic type if one can be derived from the cast.
244// Compare the current dynamic type of the region and the new type to which we
245// are casting. If the new type is lower in the inheritance hierarchy, pick it.
246const ObjCObjectPointerType *
247DynamicTypePropagation::getBetterObjCType(const Expr *CastE,
248                                          CheckerContext &C) const {
249  const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
250  assert(ToR);
251
252  // Get the old and new types.
253  const ObjCObjectPointerType *NewTy =
254      CastE->getType()->getAs<ObjCObjectPointerType>();
255  if (!NewTy)
256    return nullptr;
257  QualType OldDTy = C.getState()->getDynamicTypeInfo(ToR).getType();
258  if (OldDTy.isNull()) {
259    return NewTy;
260  }
261  const ObjCObjectPointerType *OldTy =
262    OldDTy->getAs<ObjCObjectPointerType>();
263  if (!OldTy)
264    return nullptr;
265
266  // Id the old type is 'id', the new one is more precise.
267  if (OldTy->isObjCIdType() && !NewTy->isObjCIdType())
268    return NewTy;
269
270  // Return new if it's a subclass of old.
271  const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl();
272  const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl();
273  if (ToI && FromI && FromI->isSuperClassOf(ToI))
274    return NewTy;
275
276  return nullptr;
277}
278
279void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
280  mgr.registerChecker<DynamicTypePropagation>();
281}
282