blob: 98c7e28b740f0e3c28f7ef196761bc9b326c11ca [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"
Ted Kremenekf1ae7052008-04-03 17:57:38 +000019#include "clang/Analysis/PathSensitive/GRExprEngine.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000020#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
21#include "clang/Analysis/PathSensitive/ValueState.h"
Ted Kremenekf1ae7052008-04-03 17:57:38 +000022#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000023#include "clang/Analysis/PathDiagnostic.h"
24#include "clang/AST/Expr.h"
25#include "clang/AST/ASTContext.h"
26#include "llvm/Support/Compiler.h"
27
28#include <vector>
Ted Kremenek4ba62832008-03-27 22:05:32 +000029#include <sstream>
Ted Kremenek99c6ad32008-03-27 07:25:52 +000030
31using namespace clang;
Ted Kremenek52755612008-03-27 17:17:22 +000032
Ted Kremenek4ba62832008-03-27 22:05:32 +000033static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) {
34 Expr* Receiver = ME->getReceiver();
35
36 if (!Receiver)
37 return NULL;
38
Ted Kremenek7956f752008-04-03 21:44:24 +000039 // FIXME: Cleanup
40 QualType X = Receiver->getType();
41 Type* TP = X.getTypePtr();
Ted Kremenek6bdafbf2008-04-19 19:12:50 +000042
43 // FIXME: Why can this not be a pointer type?
44 // assert (TP->isPointerType());
45 if (!TP->isPointerType())
46 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000047
Ted Kremenek7956f752008-04-03 21:44:24 +000048 const PointerType* T = TP->getAsPointerType();
Ted Kremenek4ba62832008-03-27 22:05:32 +000049
50 return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
51}
52
53static const char* GetReceiverNameType(ObjCMessageExpr* ME) {
54 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
55 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
56 : NULL;
57}
Ted Kremenek52755612008-03-27 17:17:22 +000058
Ted Kremenekf1ae7052008-04-03 17:57:38 +000059namespace {
60
Ted Kremenek95cc1ba2008-04-18 20:54:29 +000061class VISIBILITY_HIDDEN NilArg : public BugTypeCacheLocation {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000062public:
Ted Kremenekf1ae7052008-04-03 17:57:38 +000063 virtual ~NilArg() {}
64
65 virtual const char* getName() const {
66 return "nil argument";
67 }
68
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000069 class Report : public BugReport {
70 std::string Msg;
71 const char* s;
72 SourceRange R;
73 public:
Ted Kremenekf1ae7052008-04-03 17:57:38 +000074
Ted Kremenekd2f642b2008-04-14 17:39:48 +000075 Report(NilArg& Desc, ExplodedNode<ValueState>* N,
76 ObjCMessageExpr* ME, unsigned Arg)
77 : BugReport(Desc, N) {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000078
79 Expr* E = ME->getArg(Arg);
80 R = E->getSourceRange();
81
82 std::ostringstream os;
83
84 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
85 << ME->getSelector().getName() << "' cannot be nil.";
86
87 Msg = os.str();
88 s = Msg.c_str();
89 }
90
91 virtual ~Report() {}
92
Ted Kremenek4d35dac2008-04-10 16:05:13 +000093 virtual const char* getDescription() const { return s; }
94
95 virtual void getRanges(const SourceRange*& B, const SourceRange*& E) const {
96 B = &R;
97 E = B+1;
98 }
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000099 };
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000100};
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000101
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000102
103class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000104 NilArg Desc;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000105 ASTContext &Ctx;
106 ValueStateManager* VMgr;
107
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000108 typedef std::vector<BugReport*> ErrorsTy;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000109 ErrorsTy Errors;
110
111 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
112
113 bool isNSString(ObjCInterfaceType* T, const char* suffix);
114 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
115
116 void Warn(NodeTy* N, Expr* E, const std::string& s);
117 void WarnNilArg(NodeTy* N, Expr* E);
118
119 bool CheckNilArg(NodeTy* N, unsigned Arg);
120
121public:
122 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
123 : Ctx(ctx), VMgr(vmgr) {}
124
125 virtual ~BasicObjCFoundationChecks() {
126 for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000127 delete *I;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000128 }
129
130 virtual bool Audit(ExplodedNode<ValueState>* N);
131
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000132 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000133
134private:
135
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000136 void AddError(BugReport* R) {
137 Errors.push_back(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000138 }
139
140 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000141 AddError(new NilArg::Report(Desc, N, ME, Arg));
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000142 }
143};
144
145} // end anonymous namespace
146
147
148GRSimpleAPICheck*
149clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
150 ValueStateManager* VMgr) {
151
152 return new BasicObjCFoundationChecks(Ctx, VMgr);
153}
154
155
156
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000157bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
158
159 ObjCMessageExpr* ME =
160 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000161
Ted Kremenek4ba62832008-03-27 22:05:32 +0000162 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000163
164 if (!ReceiverType)
Ted Kremenek4ba62832008-03-27 22:05:32 +0000165 return NULL;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000166
Ted Kremenek4ba62832008-03-27 22:05:32 +0000167 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
168
169 if (!name)
170 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000171
172 if (name[0] != 'N' || name[1] != 'S')
173 return false;
174
175 name += 2;
176
177 // FIXME: Make all of this faster.
178
179 if (isNSString(ReceiverType, name))
180 return AuditNSString(N, ME);
181
182 return false;
183}
184
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000185static inline bool isNil(RVal X) {
186 return isa<lval::ConcreteInt>(X);
187}
188
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000189//===----------------------------------------------------------------------===//
190// Error reporting.
191//===----------------------------------------------------------------------===//
192
193
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000194void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
195
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000196 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenek75840e12008-04-18 01:56:37 +0000197 BR.EmitWarning(**I);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000198}
199
200bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
201 ObjCMessageExpr* ME =
202 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
203
204 Expr * E = ME->getArg(Arg);
205
206 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000207 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000208 return true;
209 }
210
211 return false;
212}
213
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000214//===----------------------------------------------------------------------===//
215// NSString checking.
216//===----------------------------------------------------------------------===//
217
218bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
219 const char* suffix) {
220
221 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
222}
223
224bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
225 ObjCMessageExpr* ME) {
226
227 Selector S = ME->getSelector();
228
229 if (S.isUnarySelector())
230 return false;
231
232 // FIXME: This is going to be really slow doing these checks with
233 // lexical comparisons.
234
235 std::string name = S.getName();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000236 assert (!name.empty());
237 const char* cstr = &name[0];
238 unsigned len = name.size();
Ted Kremenek4ba62832008-03-27 22:05:32 +0000239
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000240 switch (len) {
241 default:
242 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000243 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000244 if (!strcmp(cstr, "compare:"))
245 return CheckNilArg(N, 0);
246
247 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000248
249 case 15:
250 // FIXME: Checking for initWithFormat: will not work in most cases
251 // yet because [NSString alloc] returns id, not NSString*. We will
252 // need support for tracking expected-type information in the analyzer
253 // to find these errors.
254 if (!strcmp(cstr, "initWithFormat:"))
255 return CheckNilArg(N, 0);
256
257 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000258
Ted Kremenek4ba62832008-03-27 22:05:32 +0000259 case 16:
260 if (!strcmp(cstr, "compare:options:"))
261 return CheckNilArg(N, 0);
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000262
263 break;
Ted Kremenek4ba62832008-03-27 22:05:32 +0000264
265 case 22:
266 if (!strcmp(cstr, "compare:options:range:"))
267 return CheckNilArg(N, 0);
268
269 break;
270
271 case 23:
272
273 if (!strcmp(cstr, "caseInsensitiveCompare:"))
274 return CheckNilArg(N, 0);
275
276 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000277
Ted Kremenek4ba62832008-03-27 22:05:32 +0000278 case 29:
279 if (!strcmp(cstr, "compare:options:range:locale:"))
280 return CheckNilArg(N, 0);
281
282 break;
283
284 case 37:
285 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
286 return CheckNilArg(N, 0);
287
288 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000289 }
290
291 return false;
292}