aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Core/ObjCMessage.cpp
blob: 4dc000f7bdf3cafe14308e76028364168e16e56b (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
//===- ObjCMessage.cpp - Wrapper for ObjC messages and dot syntax -*- C++ -*--//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines ObjCMessage which serves as a common wrapper for ObjC
// message expressions or implicit messages for loading/storing ObjC properties.
//
//===----------------------------------------------------------------------===//

#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "clang/AST/DeclCXX.h"

using namespace clang;
using namespace ento;

QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const {
  QualType resultTy;
  bool isLVal = false;

  if (isObjCMessage()) {
    resultTy = Msg.getResultType(ctx);
  } else if (const CXXConstructExpr *Ctor =
              CallE.dyn_cast<const CXXConstructExpr *>()) {
    resultTy = Ctor->getType();
  } else {
    const CallExpr *FunctionCall = CallE.get<const CallExpr *>();

    isLVal = FunctionCall->isLValue();
    const Expr *Callee = FunctionCall->getCallee();
    if (const FunctionDecl *FD = State->getSVal(Callee, LCtx).getAsFunctionDecl())
      resultTy = FD->getResultType();
    else
      resultTy = FunctionCall->getType();
  }

  if (isLVal)
    resultTy = ctx.getPointerType(resultTy);

  return resultTy;
}

SVal CallOrObjCMessage::getFunctionCallee() const {
  assert(isFunctionCall());
  assert(!isCXXCall());
  const Expr *Fun = CallE.get<const CallExpr *>()->getCallee()->IgnoreParens();
  return State->getSVal(Fun, LCtx);
}

SVal CallOrObjCMessage::getCXXCallee() const {
  assert(isCXXCall());
  const CallExpr *ActualCall = CallE.get<const CallExpr *>();
  const Expr *callee =
    cast<CXXMemberCallExpr>(ActualCall)->getImplicitObjectArgument();
  
  // FIXME: Will eventually need to cope with member pointers.  This is
  // a limitation in getImplicitObjectArgument().
  if (!callee)
    return UnknownVal();
  
  return State->getSVal(callee, LCtx);
}

SVal
CallOrObjCMessage::getInstanceMessageReceiver(const LocationContext *LC) const {
  assert(isObjCMessage());
  return Msg.getInstanceReceiverSVal(State, LC);
}

const Decl *CallOrObjCMessage::getDecl() const {
  if (isCXXCall()) {
    const CXXMemberCallExpr *CE =
        cast<CXXMemberCallExpr>(CallE.dyn_cast<const CallExpr *>());
    assert(CE);
    return CE->getMethodDecl();
  } else if (isObjCMessage()) {
    return Msg.getMethodDecl();
  } else if (isFunctionCall()) {
    // In case of a C style call, use the path sensitive information to find
    // the function declaration.
    SVal CalleeVal = getFunctionCallee();
    return CalleeVal.getAsFunctionDecl();
  }
  return 0;
}

bool CallOrObjCMessage::isCallbackArg(unsigned Idx, const Type *T) const {
  // If the parameter is 0, it's harmless.
  if (getArgSVal(Idx).isZeroConstant())
    return false;
    
  // If a parameter is a block or a callback, assume it can modify pointer.
  if (T->isBlockPointerType() ||
      T->isFunctionPointerType() ||
      T->isObjCSelType())
    return true;

  // Check if a callback is passed inside a struct (for both, struct passed by
  // reference and by value). Dig just one level into the struct for now.
  if (const PointerType *PT = dyn_cast<PointerType>(T))
    T = PT->getPointeeType().getTypePtr();

  if (const RecordType *RT = T->getAsStructureType()) {
    const RecordDecl *RD = RT->getDecl();
    for (RecordDecl::field_iterator I = RD->field_begin(),
                                    E = RD->field_end(); I != E; ++I ) {
      const Type *FieldT = I->getType().getTypePtr();
      if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType())
        return true;
    }
  }
  return false;
}

bool CallOrObjCMessage::hasNonZeroCallbackArg() const {
  unsigned NumOfArgs = getNumArgs();

  // Process ObjC message first.
  if (!CallE) {
    const ObjCMethodDecl *D = Msg.getMethodDecl();
    unsigned Idx = 0;
    for (ObjCMethodDecl::param_const_iterator I = D->param_begin(),
                                     E = D->param_end(); I != E; ++I, ++Idx) {
      if (NumOfArgs <= Idx)
        break;

      if (isCallbackArg(Idx, (*I)->getType().getTypePtr()))
        return true;
    }
    return false;
  }

  // Else, assume we are dealing with a Function call.
  const FunctionDecl *FD = 0;
  if (const CXXConstructExpr *Ctor =
        CallE.dyn_cast<const CXXConstructExpr *>())
    FD = Ctor->getConstructor();

  const CallExpr * CE = CallE.get<const CallExpr *>();
  FD = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl());

  // If calling using a function pointer, assume the function does not
  // have a callback. TODO: We could check the types of the arguments here.
  if (!FD)
    return false;

  unsigned Idx = 0;
  for (FunctionDecl::param_const_iterator I = FD->param_begin(),
                                      E = FD->param_end(); I != E; ++I, ++Idx) {
    if (NumOfArgs <= Idx)
      break;

    if (isCallbackArg(Idx, (*I)->getType().getTypePtr()))
      return true;
  }
  return false;
}

bool CallOrObjCMessage::isCFCGAllowingEscape(StringRef FName) {
  if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G'))
         if (StrInStrNoCase(FName, "InsertValue") != StringRef::npos||
             StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
             StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
             StrInStrNoCase(FName, "WithData") != StringRef::npos ||
             StrInStrNoCase(FName, "AppendValue") != StringRef::npos||
             StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) {
       return true;
     }
  return false;
}