blob: 958f85dff9a2bba66837813aca4f1e9aef2215eb [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 Kremenekd2f642b2008-04-14 17:39:48 +000071 Report(NilArg& Desc, ExplodedNode<ValueState>* N,
72 ObjCMessageExpr* ME, unsigned Arg)
73 : BugReport(Desc, N) {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000074
75 Expr* E = ME->getArg(Arg);
76 R = E->getSourceRange();
77
78 std::ostringstream os;
79
80 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
81 << ME->getSelector().getName() << "' cannot be nil.";
82
83 Msg = os.str();
84 s = Msg.c_str();
85 }
86
87 virtual ~Report() {}
88
Ted Kremenek4d35dac2008-04-10 16:05:13 +000089 virtual const char* getDescription() const { return s; }
90
91 virtual void getRanges(const SourceRange*& B, const SourceRange*& E) const {
92 B = &R;
93 E = B+1;
94 }
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000095 };
Ted Kremenekf1ae7052008-04-03 17:57:38 +000096};
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000097
Ted Kremenekf1ae7052008-04-03 17:57:38 +000098
99class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000100 NilArg Desc;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000101 ASTContext &Ctx;
102 ValueStateManager* VMgr;
103
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000104 typedef std::vector<BugReport*> ErrorsTy;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000105 ErrorsTy Errors;
106
107 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
108
109 bool isNSString(ObjCInterfaceType* T, const char* suffix);
110 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
111
112 void Warn(NodeTy* N, Expr* E, const std::string& s);
113 void WarnNilArg(NodeTy* N, Expr* E);
114
115 bool CheckNilArg(NodeTy* N, unsigned Arg);
116
117public:
118 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
119 : Ctx(ctx), VMgr(vmgr) {}
120
121 virtual ~BasicObjCFoundationChecks() {
122 for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000123 delete *I;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000124 }
125
126 virtual bool Audit(ExplodedNode<ValueState>* N);
127
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000128 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000129
130private:
131
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000132 void AddError(BugReport* R) {
133 Errors.push_back(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000134 }
135
136 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000137 AddError(new NilArg::Report(Desc, N, ME, Arg));
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000138 }
139};
140
141} // end anonymous namespace
142
143
144GRSimpleAPICheck*
145clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
146 ValueStateManager* VMgr) {
147
148 return new BasicObjCFoundationChecks(Ctx, VMgr);
149}
150
151
152
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000153bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
154
155 ObjCMessageExpr* ME =
156 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000157
Ted Kremenek4ba62832008-03-27 22:05:32 +0000158 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000159
160 if (!ReceiverType)
Ted Kremenek4ba62832008-03-27 22:05:32 +0000161 return NULL;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000162
Ted Kremenek4ba62832008-03-27 22:05:32 +0000163 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
164
165 if (!name)
166 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000167
168 if (name[0] != 'N' || name[1] != 'S')
169 return false;
170
171 name += 2;
172
173 // FIXME: Make all of this faster.
174
175 if (isNSString(ReceiverType, name))
176 return AuditNSString(N, ME);
177
178 return false;
179}
180
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000181static inline bool isNil(RVal X) {
182 return isa<lval::ConcreteInt>(X);
183}
184
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000185//===----------------------------------------------------------------------===//
186// Error reporting.
187//===----------------------------------------------------------------------===//
188
189
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000190void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
191
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000192 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenek75840e12008-04-18 01:56:37 +0000193 BR.EmitWarning(**I);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000194}
195
196bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
197 ObjCMessageExpr* ME =
198 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
199
200 Expr * E = ME->getArg(Arg);
201
202 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000203 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000204 return true;
205 }
206
207 return false;
208}
209
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000210//===----------------------------------------------------------------------===//
211// NSString checking.
212//===----------------------------------------------------------------------===//
213
214bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
215 const char* suffix) {
216
217 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
218}
219
220bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
221 ObjCMessageExpr* ME) {
222
223 Selector S = ME->getSelector();
224
225 if (S.isUnarySelector())
226 return false;
227
228 // FIXME: This is going to be really slow doing these checks with
229 // lexical comparisons.
230
231 std::string name = S.getName();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000232 assert (!name.empty());
233 const char* cstr = &name[0];
234 unsigned len = name.size();
Ted Kremenek4ba62832008-03-27 22:05:32 +0000235
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000236 switch (len) {
237 default:
238 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000239 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000240 if (!strcmp(cstr, "compare:"))
241 return CheckNilArg(N, 0);
242
243 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000244
245 case 15:
246 // FIXME: Checking for initWithFormat: will not work in most cases
247 // yet because [NSString alloc] returns id, not NSString*. We will
248 // need support for tracking expected-type information in the analyzer
249 // to find these errors.
250 if (!strcmp(cstr, "initWithFormat:"))
251 return CheckNilArg(N, 0);
252
253 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000254
Ted Kremenek4ba62832008-03-27 22:05:32 +0000255 case 16:
256 if (!strcmp(cstr, "compare:options:"))
257 return CheckNilArg(N, 0);
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000258
259 break;
Ted Kremenek4ba62832008-03-27 22:05:32 +0000260
261 case 22:
262 if (!strcmp(cstr, "compare:options:range:"))
263 return CheckNilArg(N, 0);
264
265 break;
266
267 case 23:
268
269 if (!strcmp(cstr, "caseInsensitiveCompare:"))
270 return CheckNilArg(N, 0);
271
272 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000273
Ted Kremenek4ba62832008-03-27 22:05:32 +0000274 case 29:
275 if (!strcmp(cstr, "compare:options:range:locale:"))
276 return CheckNilArg(N, 0);
277
278 break;
279
280 case 37:
281 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
282 return CheckNilArg(N, 0);
283
284 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000285 }
286
287 return false;
288}