blob: e5a31bf50dfc1a9416dd871d73b33da9affb27d8 [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();
42 assert (TP->isPointerType());
Ted Kremenek4ba62832008-03-27 22:05:32 +000043
Ted Kremenek7956f752008-04-03 21:44:24 +000044 const PointerType* T = TP->getAsPointerType();
Ted Kremenek4ba62832008-03-27 22:05:32 +000045
46 return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
47}
48
49static const char* GetReceiverNameType(ObjCMessageExpr* ME) {
50 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
51 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
52 : NULL;
53}
Ted Kremenek52755612008-03-27 17:17:22 +000054
Ted Kremenekf1ae7052008-04-03 17:57:38 +000055namespace {
56
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000057class VISIBILITY_HIDDEN NilArg : public BugType {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000058public:
Ted Kremenekf1ae7052008-04-03 17:57:38 +000059 virtual ~NilArg() {}
60
61 virtual const char* getName() const {
62 return "nil argument";
63 }
64
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000065 class Report : public BugReport {
66 std::string Msg;
67 const char* s;
68 SourceRange R;
69 public:
Ted Kremenekf1ae7052008-04-03 17:57:38 +000070
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000071 Report(NilArg& Desc, ObjCMessageExpr* ME, unsigned Arg) : BugReport(Desc) {
72
73 Expr* E = ME->getArg(Arg);
74 R = E->getSourceRange();
75
76 std::ostringstream os;
77
78 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
79 << ME->getSelector().getName() << "' cannot be nil.";
80
81 Msg = os.str();
82 s = Msg.c_str();
83 }
84
85 virtual ~Report() {}
86
Ted Kremenek4d35dac2008-04-10 16:05:13 +000087 virtual const char* getDescription() const { return s; }
88
89 virtual void getRanges(const SourceRange*& B, const SourceRange*& E) const {
90 B = &R;
91 E = B+1;
92 }
93
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000094 virtual PathDiagnosticPiece* getEndPath(ASTContext& Ctx,
95 ExplodedNode<ValueState> *N) const {
96
97 Stmt* S = cast<PostStmt>(N->getLocation()).getStmt();
98
99 if (!S)
100 return NULL;
101
102 FullSourceLoc L(S->getLocStart(), Ctx.getSourceManager());
103 PathDiagnosticPiece* P = new PathDiagnosticPiece(L, s);
104
105 P->addRange(R);
106
107 return P;
108 }
109 };
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000110};
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000111
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000112
113class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000114 NilArg Desc;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000115 ASTContext &Ctx;
116 ValueStateManager* VMgr;
117
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000118 typedef std::vector<std::pair<NodeTy*,BugReport*> > ErrorsTy;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000119 ErrorsTy Errors;
120
121 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
122
123 bool isNSString(ObjCInterfaceType* T, const char* suffix);
124 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
125
126 void Warn(NodeTy* N, Expr* E, const std::string& s);
127 void WarnNilArg(NodeTy* N, Expr* E);
128
129 bool CheckNilArg(NodeTy* N, unsigned Arg);
130
131public:
132 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
133 : Ctx(ctx), VMgr(vmgr) {}
134
135 virtual ~BasicObjCFoundationChecks() {
136 for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
137 delete I->second;
138 }
139
140 virtual bool Audit(ExplodedNode<ValueState>* N);
141
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000142 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000143
144private:
145
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000146 void AddError(NodeTy* N, BugReport* R) {
147 Errors.push_back(std::make_pair(N, R));
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000148 }
149
150 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000151 AddError(N, new NilArg::Report(Desc, ME, Arg));
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000152 }
153};
154
155} // end anonymous namespace
156
157
158GRSimpleAPICheck*
159clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
160 ValueStateManager* VMgr) {
161
162 return new BasicObjCFoundationChecks(Ctx, VMgr);
163}
164
165
166
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000167bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
168
169 ObjCMessageExpr* ME =
170 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000171
Ted Kremenek4ba62832008-03-27 22:05:32 +0000172 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000173
174 if (!ReceiverType)
Ted Kremenek4ba62832008-03-27 22:05:32 +0000175 return NULL;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000176
Ted Kremenek4ba62832008-03-27 22:05:32 +0000177 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
178
179 if (!name)
180 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000181
182 if (name[0] != 'N' || name[1] != 'S')
183 return false;
184
185 name += 2;
186
187 // FIXME: Make all of this faster.
188
189 if (isNSString(ReceiverType, name))
190 return AuditNSString(N, ME);
191
192 return false;
193}
194
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000195static inline bool isNil(RVal X) {
196 return isa<lval::ConcreteInt>(X);
197}
198
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000199//===----------------------------------------------------------------------===//
200// Error reporting.
201//===----------------------------------------------------------------------===//
202
203
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000204void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
205
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000206 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000207
208 BR.EmitPathWarning(*I->second, I->first);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000209}
210
211bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
212 ObjCMessageExpr* ME =
213 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
214
215 Expr * E = ME->getArg(Arg);
216
217 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000218 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000219 return true;
220 }
221
222 return false;
223}
224
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000225//===----------------------------------------------------------------------===//
226// NSString checking.
227//===----------------------------------------------------------------------===//
228
229bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
230 const char* suffix) {
231
232 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
233}
234
235bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
236 ObjCMessageExpr* ME) {
237
238 Selector S = ME->getSelector();
239
240 if (S.isUnarySelector())
241 return false;
242
243 // FIXME: This is going to be really slow doing these checks with
244 // lexical comparisons.
245
246 std::string name = S.getName();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000247 assert (!name.empty());
248 const char* cstr = &name[0];
249 unsigned len = name.size();
Ted Kremenek4ba62832008-03-27 22:05:32 +0000250
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000251 switch (len) {
252 default:
253 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000254 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000255 if (!strcmp(cstr, "compare:"))
256 return CheckNilArg(N, 0);
257
258 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000259
260 case 15:
261 // FIXME: Checking for initWithFormat: will not work in most cases
262 // yet because [NSString alloc] returns id, not NSString*. We will
263 // need support for tracking expected-type information in the analyzer
264 // to find these errors.
265 if (!strcmp(cstr, "initWithFormat:"))
266 return CheckNilArg(N, 0);
267
268 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000269
Ted Kremenek4ba62832008-03-27 22:05:32 +0000270 case 16:
271 if (!strcmp(cstr, "compare:options:"))
272 return CheckNilArg(N, 0);
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000273
274 break;
Ted Kremenek4ba62832008-03-27 22:05:32 +0000275
276 case 22:
277 if (!strcmp(cstr, "compare:options:range:"))
278 return CheckNilArg(N, 0);
279
280 break;
281
282 case 23:
283
284 if (!strcmp(cstr, "caseInsensitiveCompare:"))
285 return CheckNilArg(N, 0);
286
287 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000288
Ted Kremenek4ba62832008-03-27 22:05:32 +0000289 case 29:
290 if (!strcmp(cstr, "compare:options:range:locale:"))
291 return CheckNilArg(N, 0);
292
293 break;
294
295 case 37:
296 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
297 return CheckNilArg(N, 0);
298
299 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000300 }
301
302 return false;
303}