blob: 82cabaf7a3fd4587a98182eae80c6f5e47497620 [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"
21#include "clang/Analysis/PathSensitive/AnnotatedPath.h"
22#include "clang/Analysis/PathDiagnostic.h"
23#include "clang/AST/Expr.h"
24#include "clang/AST/ASTContext.h"
25#include "llvm/Support/Compiler.h"
26
27#include <vector>
Ted Kremenek4ba62832008-03-27 22:05:32 +000028#include <sstream>
Ted Kremenek99c6ad32008-03-27 07:25:52 +000029
30using namespace clang;
31
32namespace {
33
34class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
35
Ted Kremeneke5d5c202008-03-27 21:15:17 +000036 ASTContext &Ctx;
37 ValueStateManager* VMgr;
38
39 typedef std::list<AnnotatedPath<ValueState> > ErrorsTy;
40 ErrorsTy Errors;
Ted Kremenek99c6ad32008-03-27 07:25:52 +000041
Ted Kremeneke5d5c202008-03-27 21:15:17 +000042 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
Ted Kremenek99c6ad32008-03-27 07:25:52 +000043
Ted Kremeneke5d5c202008-03-27 21:15:17 +000044 bool isNSString(ObjCInterfaceType* T, const char* suffix);
45 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
Ted Kremenek99c6ad32008-03-27 07:25:52 +000046
Ted Kremenek4ba62832008-03-27 22:05:32 +000047 void Warn(NodeTy* N, Expr* E, const std::string& s);
48 void WarnNilArg(NodeTy* N, Expr* E);
49
50 bool CheckNilArg(NodeTy* N, unsigned Arg);
Ted Kremenek99c6ad32008-03-27 07:25:52 +000051
52public:
53 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
54 : Ctx(ctx), VMgr(vmgr) {}
55
56 virtual ~BasicObjCFoundationChecks() {}
57
58 virtual bool Audit(ExplodedNode<ValueState>* N);
Ted Kremeneke5d5c202008-03-27 21:15:17 +000059
60 virtual void ReportResults(Diagnostic& D);
61
Ted Kremenek99c6ad32008-03-27 07:25:52 +000062};
63
64} // end anonymous namespace
65
66
Ted Kremenek52755612008-03-27 17:17:22 +000067GRSimpleAPICheck*
68clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
69 ValueStateManager* VMgr) {
70
71 return new BasicObjCFoundationChecks(Ctx, VMgr);
72}
73
Ted Kremenek4ba62832008-03-27 22:05:32 +000074static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) {
75 Expr* Receiver = ME->getReceiver();
76
77 if (!Receiver)
78 return NULL;
79
80 assert (Receiver->getType()->isPointerType());
81
82 const PointerType* T = Receiver->getType()->getAsPointerType();
83
84 return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
85}
86
87static const char* GetReceiverNameType(ObjCMessageExpr* ME) {
88 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
89 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
90 : NULL;
91}
Ted Kremenek52755612008-03-27 17:17:22 +000092
Ted Kremenek99c6ad32008-03-27 07:25:52 +000093bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
94
95 ObjCMessageExpr* ME =
96 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +000097
Ted Kremenek4ba62832008-03-27 22:05:32 +000098 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenek99c6ad32008-03-27 07:25:52 +000099
100 if (!ReceiverType)
Ted Kremenek4ba62832008-03-27 22:05:32 +0000101 return NULL;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000102
Ted Kremenek4ba62832008-03-27 22:05:32 +0000103 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
104
105 if (!name)
106 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000107
108 if (name[0] != 'N' || name[1] != 'S')
109 return false;
110
111 name += 2;
112
113 // FIXME: Make all of this faster.
114
115 if (isNSString(ReceiverType, name))
116 return AuditNSString(N, ME);
117
118 return false;
119}
120
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000121static inline bool isNil(RVal X) {
122 return isa<lval::ConcreteInt>(X);
123}
124
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000125//===----------------------------------------------------------------------===//
126// Error reporting.
127//===----------------------------------------------------------------------===//
128
129
Ted Kremenek4ba62832008-03-27 22:05:32 +0000130void BasicObjCFoundationChecks::Warn(NodeTy* N, Expr* E, const std::string& s) {
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000131 Errors.push_back(AnnotatedPath<ValueState>());
Ted Kremenek4ba62832008-03-27 22:05:32 +0000132 Errors.back().push_back(N, s, E);
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000133}
134
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000135void BasicObjCFoundationChecks::ReportResults(Diagnostic& D) {
136
137 // FIXME: Expand errors into paths. For now, just issue warnings.
138
139 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I) {
140
141 AnnotatedNode<ValueState>& AN = I->back();
142
143 unsigned diag = D.getCustomDiagID(Diagnostic::Warning,
144 AN.getString().c_str());
145
146 Stmt* S = cast<PostStmt>(AN.getNode()->getLocation()).getStmt();
147 FullSourceLoc L(S->getLocStart(), Ctx.getSourceManager());
148
149 SourceRange R = AN.getExpr()->getSourceRange();
150
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000151 D.Report(L, diag, &AN.getString(), 1, &R, 1);
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000152 }
153}
154
Ted Kremenek4ba62832008-03-27 22:05:32 +0000155void BasicObjCFoundationChecks::WarnNilArg(NodeTy* N, Expr* E) {
156
157 ObjCMessageExpr* ME =
158 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
159
160 std::ostringstream os;
161
162 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
163 << ME->getSelector().getName()
164 << "' cannot be nil.";
165
166 Warn(N, E, os.str());
167}
168
169bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
170 ObjCMessageExpr* ME =
171 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
172
173 Expr * E = ME->getArg(Arg);
174
175 if (isNil(GetRVal(N->getState(), E))) {
176 WarnNilArg(N, E);
177 return true;
178 }
179
180 return false;
181}
182
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000183//===----------------------------------------------------------------------===//
184// NSString checking.
185//===----------------------------------------------------------------------===//
186
187bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
188 const char* suffix) {
189
190 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
191}
192
193bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
194 ObjCMessageExpr* ME) {
195
196 Selector S = ME->getSelector();
197
198 if (S.isUnarySelector())
199 return false;
200
201 // FIXME: This is going to be really slow doing these checks with
202 // lexical comparisons.
203
204 std::string name = S.getName();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000205 assert (!name.empty());
206 const char* cstr = &name[0];
207 unsigned len = name.size();
Ted Kremenek4ba62832008-03-27 22:05:32 +0000208
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000209 switch (len) {
210 default:
211 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000212 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000213 if (!strcmp(cstr, "compare:"))
214 return CheckNilArg(N, 0);
215
216 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000217
218 case 15:
219 // FIXME: Checking for initWithFormat: will not work in most cases
220 // yet because [NSString alloc] returns id, not NSString*. We will
221 // need support for tracking expected-type information in the analyzer
222 // to find these errors.
223 if (!strcmp(cstr, "initWithFormat:"))
224 return CheckNilArg(N, 0);
225
226 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000227
Ted Kremenek4ba62832008-03-27 22:05:32 +0000228 case 16:
229 if (!strcmp(cstr, "compare:options:"))
230 return CheckNilArg(N, 0);
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000231
232 break;
Ted Kremenek4ba62832008-03-27 22:05:32 +0000233
234 case 22:
235 if (!strcmp(cstr, "compare:options:range:"))
236 return CheckNilArg(N, 0);
237
238 break;
239
240 case 23:
241
242 if (!strcmp(cstr, "caseInsensitiveCompare:"))
243 return CheckNilArg(N, 0);
244
245 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000246
Ted Kremenek4ba62832008-03-27 22:05:32 +0000247 case 29:
248 if (!strcmp(cstr, "compare:options:range:locale:"))
249 return CheckNilArg(N, 0);
250
251 break;
252
253 case 37:
254 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
255 return CheckNilArg(N, 0);
256
257 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000258 }
259
260 return false;
261}