blob: 9169492f6d91b7502c8a9c34e43bfc98fab82dc4 [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
39 assert (Receiver->getType()->isPointerType());
40
41 const PointerType* T = Receiver->getType()->getAsPointerType();
42
43 return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
44}
45
46static const char* GetReceiverNameType(ObjCMessageExpr* ME) {
47 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
48 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
49 : NULL;
50}
Ted Kremenek52755612008-03-27 17:17:22 +000051
Ted Kremenekf1ae7052008-04-03 17:57:38 +000052namespace {
53
54class VISIBILITY_HIDDEN NilArg : public BugDescription {
55 std::string Msg;
56 const char* s;
57 SourceRange R;
58public:
59 NilArg(ObjCMessageExpr* ME, unsigned Arg);
60 virtual ~NilArg() {}
61
62 virtual const char* getName() const {
63 return "nil argument";
64 }
65
66 virtual const char* getDescription() const {
67 return s;
68 }
69
70 virtual void getRanges(const SourceRange*& beg,
71 const SourceRange*& end) const {
72 beg = &R;
73 end = beg+1;
74 }
75
76};
77
78NilArg::NilArg(ObjCMessageExpr* ME, unsigned Arg) : s(NULL) {
79
80 Expr* E = ME->getArg(Arg);
81 R = E->getSourceRange();
82
83 std::ostringstream os;
84
85 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
86 << ME->getSelector().getName() << "' cannot be nil.";
87
88 Msg = os.str();
89 s = Msg.c_str();
90}
91
92
93class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
94
95 ASTContext &Ctx;
96 ValueStateManager* VMgr;
97
98 typedef std::vector<std::pair<NodeTy*,BugDescription*> > ErrorsTy;
99 ErrorsTy Errors;
100
101 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
102
103 bool isNSString(ObjCInterfaceType* T, const char* suffix);
104 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
105
106 void Warn(NodeTy* N, Expr* E, const std::string& s);
107 void WarnNilArg(NodeTy* N, Expr* E);
108
109 bool CheckNilArg(NodeTy* N, unsigned Arg);
110
111public:
112 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
113 : Ctx(ctx), VMgr(vmgr) {}
114
115 virtual ~BasicObjCFoundationChecks() {
116 for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
117 delete I->second;
118 }
119
120 virtual bool Audit(ExplodedNode<ValueState>* N);
121
122 virtual void ReportResults(Diagnostic& Diag, PathDiagnosticClient* PD,
123 ASTContext& Ctx, BugReporter& BR,
124 ExplodedGraph<GRExprEngine>& G);
125
126private:
127
128 void AddError(NodeTy* N, BugDescription* D) {
129 Errors.push_back(std::make_pair(N, D));
130 }
131
132 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
133 AddError(N, new NilArg(ME, Arg));
134 }
135};
136
137} // end anonymous namespace
138
139
140GRSimpleAPICheck*
141clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
142 ValueStateManager* VMgr) {
143
144 return new BasicObjCFoundationChecks(Ctx, VMgr);
145}
146
147
148
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000149bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
150
151 ObjCMessageExpr* ME =
152 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000153
Ted Kremenek4ba62832008-03-27 22:05:32 +0000154 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000155
156 if (!ReceiverType)
Ted Kremenek4ba62832008-03-27 22:05:32 +0000157 return NULL;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000158
Ted Kremenek4ba62832008-03-27 22:05:32 +0000159 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
160
161 if (!name)
162 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000163
164 if (name[0] != 'N' || name[1] != 'S')
165 return false;
166
167 name += 2;
168
169 // FIXME: Make all of this faster.
170
171 if (isNSString(ReceiverType, name))
172 return AuditNSString(N, ME);
173
174 return false;
175}
176
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000177static inline bool isNil(RVal X) {
178 return isa<lval::ConcreteInt>(X);
179}
180
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000181//===----------------------------------------------------------------------===//
182// Error reporting.
183//===----------------------------------------------------------------------===//
184
185
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000186void BasicObjCFoundationChecks::ReportResults(Diagnostic& Diag,
187 PathDiagnosticClient* PD,
188 ASTContext& Ctx, BugReporter& BR,
189 ExplodedGraph<GRExprEngine>& G) {
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000190
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000191 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
192 BR.EmitPathWarning(Diag, PD, Ctx, *I->second, G, I->first);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000193}
194
195bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
196 ObjCMessageExpr* ME =
197 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
198
199 Expr * E = ME->getArg(Arg);
200
201 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000202 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000203 return true;
204 }
205
206 return false;
207}
208
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000209//===----------------------------------------------------------------------===//
210// NSString checking.
211//===----------------------------------------------------------------------===//
212
213bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
214 const char* suffix) {
215
216 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
217}
218
219bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
220 ObjCMessageExpr* ME) {
221
222 Selector S = ME->getSelector();
223
224 if (S.isUnarySelector())
225 return false;
226
227 // FIXME: This is going to be really slow doing these checks with
228 // lexical comparisons.
229
230 std::string name = S.getName();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000231 assert (!name.empty());
232 const char* cstr = &name[0];
233 unsigned len = name.size();
Ted Kremenek4ba62832008-03-27 22:05:32 +0000234
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000235 switch (len) {
236 default:
237 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000238 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000239 if (!strcmp(cstr, "compare:"))
240 return CheckNilArg(N, 0);
241
242 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000243
244 case 15:
245 // FIXME: Checking for initWithFormat: will not work in most cases
246 // yet because [NSString alloc] returns id, not NSString*. We will
247 // need support for tracking expected-type information in the analyzer
248 // to find these errors.
249 if (!strcmp(cstr, "initWithFormat:"))
250 return CheckNilArg(N, 0);
251
252 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000253
Ted Kremenek4ba62832008-03-27 22:05:32 +0000254 case 16:
255 if (!strcmp(cstr, "compare:options:"))
256 return CheckNilArg(N, 0);
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000257
258 break;
Ted Kremenek4ba62832008-03-27 22:05:32 +0000259
260 case 22:
261 if (!strcmp(cstr, "compare:options:range:"))
262 return CheckNilArg(N, 0);
263
264 break;
265
266 case 23:
267
268 if (!strcmp(cstr, "caseInsensitiveCompare:"))
269 return CheckNilArg(N, 0);
270
271 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000272
Ted Kremenek4ba62832008-03-27 22:05:32 +0000273 case 29:
274 if (!strcmp(cstr, "compare:options:range:locale:"))
275 return CheckNilArg(N, 0);
276
277 break;
278
279 case 37:
280 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
281 return CheckNilArg(N, 0);
282
283 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000284 }
285
286 return false;
287}