blob: cf26d1e30bd4c2f2c0b129db259d2cda63fb0c16 [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"
Steve Narofff494b572008-05-29 21:12:08 +000024#include "clang/AST/ExprObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000025#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 QualType X = Receiver->getType();
Ted Kremenek6bdafbf2008-04-19 19:12:50 +000040
Ted Kremenekc1ff3cd2008-04-30 22:48:21 +000041 if (X->isPointerType()) {
42 Type* TP = X.getTypePtr();
43 const PointerType* T = TP->getAsPointerType();
44 return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
45 }
46
47 // FIXME: Support ObjCQualifiedIdType?
48 return NULL;
Ted Kremenek4ba62832008-03-27 22:05:32 +000049}
50
51static const char* GetReceiverNameType(ObjCMessageExpr* ME) {
52 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
53 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
54 : NULL;
55}
Ted Kremenek52755612008-03-27 17:17:22 +000056
Ted Kremenekf1ae7052008-04-03 17:57:38 +000057namespace {
58
Ted Kremenek95cc1ba2008-04-18 20:54:29 +000059class VISIBILITY_HIDDEN NilArg : public BugTypeCacheLocation {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000060public:
Ted Kremenekf1ae7052008-04-03 17:57:38 +000061 virtual ~NilArg() {}
62
63 virtual const char* getName() const {
64 return "nil argument";
65 }
66
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000067 class Report : public BugReport {
68 std::string Msg;
69 const char* s;
70 SourceRange R;
71 public:
Ted Kremenekf1ae7052008-04-03 17:57:38 +000072
Ted Kremenekd2f642b2008-04-14 17:39:48 +000073 Report(NilArg& Desc, ExplodedNode<ValueState>* N,
74 ObjCMessageExpr* ME, unsigned Arg)
75 : BugReport(Desc, N) {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000076
77 Expr* E = ME->getArg(Arg);
78 R = E->getSourceRange();
79
80 std::ostringstream os;
81
82 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
83 << ME->getSelector().getName() << "' cannot be nil.";
84
85 Msg = os.str();
86 s = Msg.c_str();
87 }
88
89 virtual ~Report() {}
90
Ted Kremenek4d35dac2008-04-10 16:05:13 +000091 virtual const char* getDescription() const { return s; }
92
Ted Kremenekbb77e9b2008-05-01 22:50:36 +000093 virtual void getRanges(BugReporter& BR,
94 const SourceRange*& B, const SourceRange*& E) {
Ted Kremenek4d35dac2008-04-10 16:05:13 +000095 B = &R;
96 E = B+1;
97 }
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000098 };
Ted Kremenekf1ae7052008-04-03 17:57:38 +000099};
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000100
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000101
102class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000103 NilArg Desc;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000104 ASTContext &Ctx;
105 ValueStateManager* VMgr;
106
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000107 typedef std::vector<BugReport*> ErrorsTy;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000108 ErrorsTy Errors;
109
110 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
111
112 bool isNSString(ObjCInterfaceType* T, const char* suffix);
113 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
114
115 void Warn(NodeTy* N, Expr* E, const std::string& s);
116 void WarnNilArg(NodeTy* N, Expr* E);
117
118 bool CheckNilArg(NodeTy* N, unsigned Arg);
119
120public:
121 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
122 : Ctx(ctx), VMgr(vmgr) {}
123
124 virtual ~BasicObjCFoundationChecks() {
125 for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000126 delete *I;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000127 }
128
129 virtual bool Audit(ExplodedNode<ValueState>* N);
130
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000131 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000132
133private:
134
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000135 void AddError(BugReport* R) {
136 Errors.push_back(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000137 }
138
139 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000140 AddError(new NilArg::Report(Desc, N, ME, Arg));
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000141 }
142};
143
144} // end anonymous namespace
145
146
147GRSimpleAPICheck*
148clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
149 ValueStateManager* VMgr) {
150
151 return new BasicObjCFoundationChecks(Ctx, VMgr);
152}
153
154
155
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000156bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
157
158 ObjCMessageExpr* ME =
159 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000160
Ted Kremenek4ba62832008-03-27 22:05:32 +0000161 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000162
163 if (!ReceiverType)
Nuno Lopesf7427942008-05-20 17:33:56 +0000164 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000165
Ted Kremenek4ba62832008-03-27 22:05:32 +0000166 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
167
168 if (!name)
169 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000170
171 if (name[0] != 'N' || name[1] != 'S')
172 return false;
173
174 name += 2;
175
176 // FIXME: Make all of this faster.
177
178 if (isNSString(ReceiverType, name))
179 return AuditNSString(N, ME);
180
Nuno Lopesf7427942008-05-20 17:33:56 +0000181 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000182}
183
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000184static inline bool isNil(RVal X) {
185 return isa<lval::ConcreteInt>(X);
186}
187
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000188//===----------------------------------------------------------------------===//
189// Error reporting.
190//===----------------------------------------------------------------------===//
191
192
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000193void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
194
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000195 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenek75840e12008-04-18 01:56:37 +0000196 BR.EmitWarning(**I);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000197}
198
199bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
200 ObjCMessageExpr* ME =
201 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
202
203 Expr * E = ME->getArg(Arg);
204
205 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000206 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000207 return true;
208 }
209
210 return false;
211}
212
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000213//===----------------------------------------------------------------------===//
214// NSString checking.
215//===----------------------------------------------------------------------===//
216
217bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
218 const char* suffix) {
219
220 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
221}
222
223bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
224 ObjCMessageExpr* ME) {
225
226 Selector S = ME->getSelector();
227
228 if (S.isUnarySelector())
229 return false;
230
231 // FIXME: This is going to be really slow doing these checks with
232 // lexical comparisons.
233
234 std::string name = S.getName();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000235 assert (!name.empty());
236 const char* cstr = &name[0];
237 unsigned len = name.size();
Ted Kremenek4ba62832008-03-27 22:05:32 +0000238
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000239 switch (len) {
240 default:
241 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000242 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000243 if (!strcmp(cstr, "compare:"))
244 return CheckNilArg(N, 0);
245
246 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000247
248 case 15:
249 // FIXME: Checking for initWithFormat: will not work in most cases
250 // yet because [NSString alloc] returns id, not NSString*. We will
251 // need support for tracking expected-type information in the analyzer
252 // to find these errors.
253 if (!strcmp(cstr, "initWithFormat:"))
254 return CheckNilArg(N, 0);
255
256 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000257
Ted Kremenek4ba62832008-03-27 22:05:32 +0000258 case 16:
259 if (!strcmp(cstr, "compare:options:"))
260 return CheckNilArg(N, 0);
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000261
262 break;
Ted Kremenek4ba62832008-03-27 22:05:32 +0000263
264 case 22:
265 if (!strcmp(cstr, "compare:options:range:"))
266 return CheckNilArg(N, 0);
267
268 break;
269
270 case 23:
271
272 if (!strcmp(cstr, "caseInsensitiveCompare:"))
273 return CheckNilArg(N, 0);
274
275 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000276
Ted Kremenek4ba62832008-03-27 22:05:32 +0000277 case 29:
278 if (!strcmp(cstr, "compare:options:range:locale:"))
279 return CheckNilArg(N, 0);
280
281 break;
282
283 case 37:
284 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
285 return CheckNilArg(N, 0);
286
287 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000288 }
289
290 return false;
291}