blob: d2a3d08b8e3eff9c6a0e5c4cf6e42c4b1a36cd9e [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
Ted Kremenekc0c3f5d2008-04-30 20:17:27 +000043 assert (IsPointerType(X));
Ted Kremenek4ba62832008-03-27 22:05:32 +000044
Ted Kremenek7956f752008-04-03 21:44:24 +000045 const PointerType* T = TP->getAsPointerType();
Ted Kremenek4ba62832008-03-27 22:05:32 +000046
47 return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
48}
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
92 virtual void getRanges(const SourceRange*& B, const SourceRange*& E) const {
93 B = &R;
94 E = B+1;
95 }
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000096 };
Ted Kremenekf1ae7052008-04-03 17:57:38 +000097};
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000098
Ted Kremenekf1ae7052008-04-03 17:57:38 +000099
100class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000101 NilArg Desc;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000102 ASTContext &Ctx;
103 ValueStateManager* VMgr;
104
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000105 typedef std::vector<BugReport*> ErrorsTy;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000106 ErrorsTy Errors;
107
108 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
109
110 bool isNSString(ObjCInterfaceType* T, const char* suffix);
111 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
112
113 void Warn(NodeTy* N, Expr* E, const std::string& s);
114 void WarnNilArg(NodeTy* N, Expr* E);
115
116 bool CheckNilArg(NodeTy* N, unsigned Arg);
117
118public:
119 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
120 : Ctx(ctx), VMgr(vmgr) {}
121
122 virtual ~BasicObjCFoundationChecks() {
123 for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000124 delete *I;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000125 }
126
127 virtual bool Audit(ExplodedNode<ValueState>* N);
128
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000129 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000130
131private:
132
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000133 void AddError(BugReport* R) {
134 Errors.push_back(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000135 }
136
137 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000138 AddError(new NilArg::Report(Desc, N, ME, Arg));
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000139 }
140};
141
142} // end anonymous namespace
143
144
145GRSimpleAPICheck*
146clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
147 ValueStateManager* VMgr) {
148
149 return new BasicObjCFoundationChecks(Ctx, VMgr);
150}
151
152
153
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000154bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
155
156 ObjCMessageExpr* ME =
157 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000158
Ted Kremenek4ba62832008-03-27 22:05:32 +0000159 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000160
161 if (!ReceiverType)
Ted Kremenek4ba62832008-03-27 22:05:32 +0000162 return NULL;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000163
Ted Kremenek4ba62832008-03-27 22:05:32 +0000164 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
165
166 if (!name)
167 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000168
169 if (name[0] != 'N' || name[1] != 'S')
170 return false;
171
172 name += 2;
173
174 // FIXME: Make all of this faster.
175
176 if (isNSString(ReceiverType, name))
177 return AuditNSString(N, ME);
178
179 return false;
180}
181
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000182static inline bool isNil(RVal X) {
183 return isa<lval::ConcreteInt>(X);
184}
185
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000186//===----------------------------------------------------------------------===//
187// Error reporting.
188//===----------------------------------------------------------------------===//
189
190
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000191void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
192
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000193 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenek75840e12008-04-18 01:56:37 +0000194 BR.EmitWarning(**I);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000195}
196
197bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
198 ObjCMessageExpr* ME =
199 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
200
201 Expr * E = ME->getArg(Arg);
202
203 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000204 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000205 return true;
206 }
207
208 return false;
209}
210
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000211//===----------------------------------------------------------------------===//
212// NSString checking.
213//===----------------------------------------------------------------------===//
214
215bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
216 const char* suffix) {
217
218 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
219}
220
221bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
222 ObjCMessageExpr* ME) {
223
224 Selector S = ME->getSelector();
225
226 if (S.isUnarySelector())
227 return false;
228
229 // FIXME: This is going to be really slow doing these checks with
230 // lexical comparisons.
231
232 std::string name = S.getName();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000233 assert (!name.empty());
234 const char* cstr = &name[0];
235 unsigned len = name.size();
Ted Kremenek4ba62832008-03-27 22:05:32 +0000236
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000237 switch (len) {
238 default:
239 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000240 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000241 if (!strcmp(cstr, "compare:"))
242 return CheckNilArg(N, 0);
243
244 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000245
246 case 15:
247 // FIXME: Checking for initWithFormat: will not work in most cases
248 // yet because [NSString alloc] returns id, not NSString*. We will
249 // need support for tracking expected-type information in the analyzer
250 // to find these errors.
251 if (!strcmp(cstr, "initWithFormat:"))
252 return CheckNilArg(N, 0);
253
254 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000255
Ted Kremenek4ba62832008-03-27 22:05:32 +0000256 case 16:
257 if (!strcmp(cstr, "compare:options:"))
258 return CheckNilArg(N, 0);
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000259
260 break;
Ted Kremenek4ba62832008-03-27 22:05:32 +0000261
262 case 22:
263 if (!strcmp(cstr, "compare:options:range:"))
264 return CheckNilArg(N, 0);
265
266 break;
267
268 case 23:
269
270 if (!strcmp(cstr, "caseInsensitiveCompare:"))
271 return CheckNilArg(N, 0);
272
273 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000274
Ted Kremenek4ba62832008-03-27 22:05:32 +0000275 case 29:
276 if (!strcmp(cstr, "compare:options:range:locale:"))
277 return CheckNilArg(N, 0);
278
279 break;
280
281 case 37:
282 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
283 return CheckNilArg(N, 0);
284
285 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000286 }
287
288 return false;
289}