blob: d158067e56da09b54bece08bad69c611a92cb692 [file] [log] [blame]
Ted Kremenekb0a2e472008-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 Kremenek7a681942008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenekb0a2e472008-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>
28
29using namespace clang;
30
31namespace {
32
33class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
34
Ted Kremenek583c4382008-03-27 21:15:17 +000035 ASTContext &Ctx;
36 ValueStateManager* VMgr;
37
38 typedef std::list<AnnotatedPath<ValueState> > ErrorsTy;
39 ErrorsTy Errors;
Ted Kremenekb0a2e472008-03-27 07:25:52 +000040
Ted Kremenek583c4382008-03-27 21:15:17 +000041 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
Ted Kremenekb0a2e472008-03-27 07:25:52 +000042
Ted Kremenek583c4382008-03-27 21:15:17 +000043 bool isNSString(ObjCInterfaceType* T, const char* suffix);
44 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
Ted Kremenekb0a2e472008-03-27 07:25:52 +000045
Ted Kremenekb20dccc2008-03-27 21:23:57 +000046 void Warn(NodeTy* N, Expr* E, const char *msg);
Ted Kremenekb0a2e472008-03-27 07:25:52 +000047
48public:
49 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
50 : Ctx(ctx), VMgr(vmgr) {}
51
52 virtual ~BasicObjCFoundationChecks() {}
53
54 virtual bool Audit(ExplodedNode<ValueState>* N);
Ted Kremenek583c4382008-03-27 21:15:17 +000055
56 virtual void ReportResults(Diagnostic& D);
57
Ted Kremenekb0a2e472008-03-27 07:25:52 +000058};
59
60} // end anonymous namespace
61
62
Ted Kremenek7a681942008-03-27 17:17:22 +000063GRSimpleAPICheck*
64clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
65 ValueStateManager* VMgr) {
66
67 return new BasicObjCFoundationChecks(Ctx, VMgr);
68}
69
70
Ted Kremenekb0a2e472008-03-27 07:25:52 +000071bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
72
73 ObjCMessageExpr* ME =
74 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
75
76 Expr* Receiver = ME->getReceiver();
77
78 if (!Receiver)
79 return false;
80
81 assert (Receiver->getType()->isPointerType());
82
83 const PointerType* T = Receiver->getType()->getAsPointerType();
84
85 ObjCInterfaceType* ReceiverType =
86 dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
87
88 if (!ReceiverType)
89 return false;
90
91 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
92
93 if (name[0] != 'N' || name[1] != 'S')
94 return false;
95
96 name += 2;
97
98 // FIXME: Make all of this faster.
99
100 if (isNSString(ReceiverType, name))
101 return AuditNSString(N, ME);
102
103 return false;
104}
105
Ted Kremenek583c4382008-03-27 21:15:17 +0000106static inline bool isNil(RVal X) {
107 return isa<lval::ConcreteInt>(X);
108}
109
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000110//===----------------------------------------------------------------------===//
111// Error reporting.
112//===----------------------------------------------------------------------===//
113
114
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000115void BasicObjCFoundationChecks::Warn(NodeTy* N,
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000116 Expr* E, const char *msg) {
117
118 Errors.push_back(AnnotatedPath<ValueState>());
119 Errors.back().push_back(N, msg, E);
120}
121
Ted Kremenek583c4382008-03-27 21:15:17 +0000122void BasicObjCFoundationChecks::ReportResults(Diagnostic& D) {
123
124 // FIXME: Expand errors into paths. For now, just issue warnings.
125
126 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I) {
127
128 AnnotatedNode<ValueState>& AN = I->back();
129
130 unsigned diag = D.getCustomDiagID(Diagnostic::Warning,
131 AN.getString().c_str());
132
133 Stmt* S = cast<PostStmt>(AN.getNode()->getLocation()).getStmt();
134 FullSourceLoc L(S->getLocStart(), Ctx.getSourceManager());
135
136 SourceRange R = AN.getExpr()->getSourceRange();
137
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000138 D.Report(L, diag, &AN.getString(), 1, &R, 1);
Ted Kremenek583c4382008-03-27 21:15:17 +0000139 }
140}
141
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000142//===----------------------------------------------------------------------===//
143// NSString checking.
144//===----------------------------------------------------------------------===//
145
146bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
147 const char* suffix) {
148
149 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
150}
151
152bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
153 ObjCMessageExpr* ME) {
154
155 Selector S = ME->getSelector();
156
157 if (S.isUnarySelector())
158 return false;
159
160 // FIXME: This is going to be really slow doing these checks with
161 // lexical comparisons.
162
163 std::string name = S.getName();
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000164 assert (!name.empty());
165 const char* cstr = &name[0];
166 unsigned len = name.size();
167
168
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000169 ValueState* St = N->getState();
170
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000171 switch (len) {
172 default:
173 break;
174 case 8:
175 if (!strcmp(cstr, "compare:")) {
176 // Check if the compared NSString is nil.
177 Expr * E = ME->getArg(0);
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000178
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000179 if (isNil(GetRVal(St, E))) {
180 Warn(N, E, "Argument to NSString method 'compare:' cannot be nil.");
181 return false;
182 }
183
184 break;
185 }
186
187 break;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000188 }
189
190 return false;
191}