blob: 8e8feec1d1a36a2bfb7be6a8bb05b915703f02ad [file] [log] [blame]
Ted Kremenek99c6ad32008-03-27 07:25:52 +00001//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- 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 file defines BasicObjCFoundationChecks, a class that encapsulates
11// a set of simple checks to run on Objective-C code using Apple's Foundation
12// classes.
13//
14//===----------------------------------------------------------------------===//
15
Ted Kremenek52755612008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenek99c6ad32008-03-27 07:25:52 +000018#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
19#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
20#include "clang/Analysis/PathSensitive/ValueState.h"
Ted Kremenekf1ae7052008-04-03 17:57:38 +000021#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000022#include "clang/Analysis/PathDiagnostic.h"
23#include "clang/AST/Expr.h"
24#include "clang/AST/ASTContext.h"
25#include "llvm/Support/Compiler.h"
26
27#include <vector>
Ted Kremenek4ba62832008-03-27 22:05:32 +000028#include <sstream>
Ted Kremenek99c6ad32008-03-27 07:25:52 +000029
30using namespace clang;
Ted Kremenek52755612008-03-27 17:17:22 +000031
Ted Kremenek4ba62832008-03-27 22:05:32 +000032static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) {
33 Expr* Receiver = ME->getReceiver();
34
35 if (!Receiver)
36 return NULL;
37
Ted Kremenek7956f752008-04-03 21:44:24 +000038 QualType X = Receiver->getType();
Ted Kremenek6bdafbf2008-04-19 19:12:50 +000039
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000040 if (X->isPointerType()) {
41 Type* TP = X.getTypePtr();
42 const PointerType* T = TP->getAsPointerType();
43 return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
44 }
45
46 // FIXME: Support ObjCQualifiedIdType?
47 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000048}
49
50static const char* GetReceiverNameType(ObjCMessageExpr* ME) {
51 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
52 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
53 : NULL;
54}
Ted Kremenek52755612008-03-27 17:17:22 +000055
Ted Kremenekf1ae7052008-04-03 17:57:38 +000056namespace {
57
Ted Kremenek95cc1ba2008-04-18 20:54:29 +000058class VISIBILITY_HIDDEN NilArg : public BugTypeCacheLocation {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000059public:
Ted Kremenekf1ae7052008-04-03 17:57:38 +000060 virtual ~NilArg() {}
61
62 virtual const char* getName() const {
63 return "nil argument";
64 }
65
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000066 class Report : public BugReport {
67 std::string Msg;
68 const char* s;
69 SourceRange R;
70 public:
Ted Kremenekf1ae7052008-04-03 17:57:38 +000071
Ted Kremenekd2f642b2008-04-14 17:39:48 +000072 Report(NilArg& Desc, ExplodedNode<ValueState>* N,
73 ObjCMessageExpr* ME, unsigned Arg)
74 : BugReport(Desc, N) {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000075
76 Expr* E = ME->getArg(Arg);
77 R = E->getSourceRange();
78
79 std::ostringstream os;
80
81 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
82 << ME->getSelector().getName() << "' cannot be nil.";
83
84 Msg = os.str();
85 s = Msg.c_str();
86 }
87
88 virtual ~Report() {}
89
Ted Kremenek4d35dac2008-04-10 16:05:13 +000090 virtual const char* getDescription() const { return s; }
91
Ted Kremenekbb77e9b2008-05-01 22:50:36 +000092 virtual void getRanges(BugReporter& BR,
93 const SourceRange*& B, const SourceRange*& E) {
Ted Kremenek4d35dac2008-04-10 16:05:13 +000094 B = &R;
95 E = B+1;
96 }
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000097 };
Ted Kremenekf1ae7052008-04-03 17:57:38 +000098};
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000099
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000100
101class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000102 NilArg Desc;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000103 ASTContext &Ctx;
104 ValueStateManager* VMgr;
105
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000106 typedef std::vector<BugReport*> ErrorsTy;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000107 ErrorsTy Errors;
108
109 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
110
111 bool isNSString(ObjCInterfaceType* T, const char* suffix);
112 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
113
114 void Warn(NodeTy* N, Expr* E, const std::string& s);
115 void WarnNilArg(NodeTy* N, Expr* E);
116
117 bool CheckNilArg(NodeTy* N, unsigned Arg);
118
119public:
120 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
121 : Ctx(ctx), VMgr(vmgr) {}
122
123 virtual ~BasicObjCFoundationChecks() {
124 for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000125 delete *I;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000126 }
127
128 virtual bool Audit(ExplodedNode<ValueState>* N);
129
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000130 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000131
132private:
133
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000134 void AddError(BugReport* R) {
135 Errors.push_back(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000136 }
137
138 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000139 AddError(new NilArg::Report(Desc, N, ME, Arg));
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000140 }
141};
142
143} // end anonymous namespace
144
145
146GRSimpleAPICheck*
147clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
148 ValueStateManager* VMgr) {
149
150 return new BasicObjCFoundationChecks(Ctx, VMgr);
151}
152
153
154
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000155bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
156
157 ObjCMessageExpr* ME =
158 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000159
Ted Kremenek4ba62832008-03-27 22:05:32 +0000160 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000161
162 if (!ReceiverType)
Ted Kremenek4ba62832008-03-27 22:05:32 +0000163 return NULL;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000164
Ted Kremenek4ba62832008-03-27 22:05:32 +0000165 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
166
167 if (!name)
168 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000169
170 if (name[0] != 'N' || name[1] != 'S')
171 return false;
172
173 name += 2;
174
175 // FIXME: Make all of this faster.
176
177 if (isNSString(ReceiverType, name))
178 return AuditNSString(N, ME);
179
180 return false;
181}
182
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000183static inline bool isNil(RVal X) {
184 return isa<lval::ConcreteInt>(X);
185}
186
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000187//===----------------------------------------------------------------------===//
188// Error reporting.
189//===----------------------------------------------------------------------===//
190
191
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000192void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
193
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000194 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenek75840e12008-04-18 01:56:37 +0000195 BR.EmitWarning(**I);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000196}
197
198bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
199 ObjCMessageExpr* ME =
200 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
201
202 Expr * E = ME->getArg(Arg);
203
204 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000205 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000206 return true;
207 }
208
209 return false;
210}
211
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000212//===----------------------------------------------------------------------===//
213// NSString checking.
214//===----------------------------------------------------------------------===//
215
216bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
217 const char* suffix) {
218
219 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
220}
221
222bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
223 ObjCMessageExpr* ME) {
224
225 Selector S = ME->getSelector();
226
227 if (S.isUnarySelector())
228 return false;
229
230 // FIXME: This is going to be really slow doing these checks with
231 // lexical comparisons.
232
233 std::string name = S.getName();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000234 assert (!name.empty());
235 const char* cstr = &name[0];
236 unsigned len = name.size();
Ted Kremenek4ba62832008-03-27 22:05:32 +0000237
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000238 switch (len) {
239 default:
240 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000241 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000242 if (!strcmp(cstr, "compare:"))
243 return CheckNilArg(N, 0);
244
245 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000246
247 case 15:
248 // FIXME: Checking for initWithFormat: will not work in most cases
249 // yet because [NSString alloc] returns id, not NSString*. We will
250 // need support for tracking expected-type information in the analyzer
251 // to find these errors.
252 if (!strcmp(cstr, "initWithFormat:"))
253 return CheckNilArg(N, 0);
254
255 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000256
Ted Kremenek4ba62832008-03-27 22:05:32 +0000257 case 16:
258 if (!strcmp(cstr, "compare:options:"))
259 return CheckNilArg(N, 0);
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000260
261 break;
Ted Kremenek4ba62832008-03-27 22:05:32 +0000262
263 case 22:
264 if (!strcmp(cstr, "compare:options:range:"))
265 return CheckNilArg(N, 0);
266
267 break;
268
269 case 23:
270
271 if (!strcmp(cstr, "caseInsensitiveCompare:"))
272 return CheckNilArg(N, 0);
273
274 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000275
Ted Kremenek4ba62832008-03-27 22:05:32 +0000276 case 29:
277 if (!strcmp(cstr, "compare:options:range:locale:"))
278 return CheckNilArg(N, 0);
279
280 break;
281
282 case 37:
283 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
284 return CheckNilArg(N, 0);
285
286 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000287 }
288
289 return false;
290}