aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
blob: d9edab8ede1602251a0f8669782bee81d32cf0f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//== DynamicTypePropagation.cpp ----------------------------------- -*- C++ -*--=//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This checker defines the rules for dynamic type gathering and propagation.
//
//===----------------------------------------------------------------------===//

#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::PostCall,
                    check::PostStmt<ImplicitCastExpr> > {
  const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
                                                    CheckerContext &C) const;

  /// \brief Return a better dynamic type if one can be derived from the cast.
  const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE,
                                                 CheckerContext &C) const;
public:
  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
  void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const;
};
}

void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
                                           CheckerContext &C) const {
  // We can obtain perfect type info for return values from some calls.
  if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {

    // Get the returned value if it's a region.
    SVal Result = C.getSVal(Call.getOriginExpr());
    const MemRegion *RetReg = Result.getAsRegion();
    if (!RetReg)
      return;

    ProgramStateRef State = C.getState();

    switch (Msg->getMethodFamily()) {
    default:
      break;

    // We assume that the type of the object returned by alloc and new are the
    // pointer to the object of the class specified in the receiver of the
    // message.
    case OMF_alloc:
    case OMF_new: {
      // Get the type of object that will get created.
      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->addDynamicTypeInfo(RetReg, DynResTy));
      break;
    }
    case OMF_init: {
      // Assume, the result of the init method has the same dynamic type as
      // the receiver and propagate the dynamic type info.
      const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion();
      if (!RecReg)
        return;
      DynamicTypeInfo RecDynType = State->getDynamicTypeInfo(RecReg);
      C.addTransition(State->addDynamicTypeInfo(RetReg, RecDynType));
      break;
    }
    }
  }
}

void DynamicTypePropagation::checkPostStmt(const ImplicitCastExpr *CastE,
                                           CheckerContext &C) const {
  // We only track dynamic type info for regions.
  const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
  if (!ToR)
    return;

  switch (CastE->getCastKind()) {
  default:
    break;
  case CK_BitCast:
    // Only handle ObjCObjects for now.
    if (const Type *NewTy = getBetterObjCType(CastE, C))
      C.addTransition(C.getState()->addDynamicTypeInfo(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.getCurrentStackFrame();
    // Are we calling [self alloc]? If this is self, get the type of the
    // enclosing ObjC class.
    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;
}

// Return a better dynamic type if one can be derived from the cast.
// Compare the current dynamic type of the region and the new type to which we
// are casting. If the new type is lower in the inheritance hierarchy, pick it.
const ObjCObjectPointerType *
DynamicTypePropagation::getBetterObjCType(const Expr *CastE,
                                          CheckerContext &C) const {
  const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
  assert(ToR);

  // Get the old and new types.
  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;

  // Id the old type is 'id', the new one is more precise.
  if (OldTy->isObjCIdType() && !NewTy->isObjCIdType())
    return NewTy;

  // Return new if it's a subclass of old.
  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>();
}