Newer
Older
Ted Kremenek
committed
//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines BasicObjCFoundationChecks, a class that encapsulates
// a set of simple checks to run on Objective-C code using Apple's Foundation
// classes.
//
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
#include "BasicObjCFoundationChecks.h"
Ted Kremenek
committed
#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
Ted Kremenek
committed
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
Ted Kremenek
committed
#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
#include "clang/Analysis/PathSensitive/ValueState.h"
Ted Kremenek
committed
#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenek
committed
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ASTContext.h"
#include "llvm/Support/Compiler.h"
#include <vector>
#include <sstream>
Ted Kremenek
committed
using namespace clang;
Ted Kremenek
committed
static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) {
Expr* Receiver = ME->getReceiver();
if (!Receiver)
return NULL;
// FIXME: Cleanup
QualType X = Receiver->getType();
Type* TP = X.getTypePtr();
assert (TP->isPointerType());
Ted Kremenek
committed
const PointerType* T = TP->getAsPointerType();
Ted Kremenek
committed
Ted Kremenek
committed
return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
}
static const char* GetReceiverNameType(ObjCMessageExpr* ME) {
ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
: NULL;
}
Ted Kremenek
committed
namespace {
Ted Kremenek
committed
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
class VISIBILITY_HIDDEN NilArg : public BugDescription {
std::string Msg;
const char* s;
SourceRange R;
public:
NilArg(ObjCMessageExpr* ME, unsigned Arg);
virtual ~NilArg() {}
virtual const char* getName() const {
return "nil argument";
}
virtual const char* getDescription() const {
return s;
}
virtual void getRanges(const SourceRange*& beg,
const SourceRange*& end) const {
beg = &R;
end = beg+1;
}
};
NilArg::NilArg(ObjCMessageExpr* ME, unsigned Arg) : s(NULL) {
Expr* E = ME->getArg(Arg);
R = E->getSourceRange();
std::ostringstream os;
os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
<< ME->getSelector().getName() << "' cannot be nil.";
Msg = os.str();
s = Msg.c_str();
}
Ted Kremenek
committed
class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
ASTContext &Ctx;
ValueStateManager* VMgr;
Ted Kremenek
committed
typedef std::vector<std::pair<NodeTy*,BugDescription*> > ErrorsTy;
ErrorsTy Errors;
Ted Kremenek
committed
RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
Ted Kremenek
committed
bool isNSString(ObjCInterfaceType* T, const char* suffix);
bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
Ted Kremenek
committed
void Warn(NodeTy* N, Expr* E, const std::string& s);
void WarnNilArg(NodeTy* N, Expr* E);
bool CheckNilArg(NodeTy* N, unsigned Arg);
Ted Kremenek
committed
public:
BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
: Ctx(ctx), VMgr(vmgr) {}
Ted Kremenek
committed
virtual ~BasicObjCFoundationChecks() {
for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
delete I->second;
}
Ted Kremenek
committed
virtual bool Audit(ExplodedNode<ValueState>* N);
Ted Kremenek
committed
virtual void ReportResults(Diagnostic& Diag, PathDiagnosticClient* PD,
ASTContext& Ctx, BugReporter& BR,
ExplodedGraph<GRExprEngine>& G);
private:
void AddError(NodeTy* N, BugDescription* D) {
Errors.push_back(std::make_pair(N, D));
}
void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
AddError(N, new NilArg(ME, Arg));
}
Ted Kremenek
committed
};
} // end anonymous namespace
Ted Kremenek
committed
GRSimpleAPICheck*
clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
ValueStateManager* VMgr) {
return new BasicObjCFoundationChecks(Ctx, VMgr);
}
Ted Kremenek
committed
bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
ObjCMessageExpr* ME =
cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenek
committed
if (!ReceiverType)
return NULL;
const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
Ted Kremenek
committed
if (!name)
return false;
Ted Kremenek
committed
if (name[0] != 'N' || name[1] != 'S')
return false;
name += 2;
// FIXME: Make all of this faster.
if (isNSString(ReceiverType, name))
return AuditNSString(N, ME);
return false;
}
static inline bool isNil(RVal X) {
return isa<lval::ConcreteInt>(X);
}
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Error reporting.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
void BasicObjCFoundationChecks::ReportResults(Diagnostic& Diag,
PathDiagnosticClient* PD,
ASTContext& Ctx, BugReporter& BR,
ExplodedGraph<GRExprEngine>& G) {
Ted Kremenek
committed
for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
BR.EmitPathWarning(Diag, PD, Ctx, *I->second, G, I->first);
}
bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
ObjCMessageExpr* ME =
cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Expr * E = ME->getArg(Arg);
if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenek
committed
WarnNilArg(N, ME, Arg);
return true;
}
return false;
}
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// NSString checking.
//===----------------------------------------------------------------------===//
bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
const char* suffix) {
return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
}
bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
ObjCMessageExpr* ME) {
Selector S = ME->getSelector();
if (S.isUnarySelector())
return false;
// FIXME: This is going to be really slow doing these checks with
// lexical comparisons.
std::string name = S.getName();
assert (!name.empty());
const char* cstr = &name[0];
unsigned len = name.size();
switch (len) {
default:
break;
Ted Kremenek
committed
case 8:
if (!strcmp(cstr, "compare:"))
return CheckNilArg(N, 0);
break;
Ted Kremenek
committed
case 15:
// FIXME: Checking for initWithFormat: will not work in most cases
// yet because [NSString alloc] returns id, not NSString*. We will
// need support for tracking expected-type information in the analyzer
// to find these errors.
if (!strcmp(cstr, "initWithFormat:"))
return CheckNilArg(N, 0);
break;
Ted Kremenek
committed
case 16:
if (!strcmp(cstr, "compare:options:"))
return CheckNilArg(N, 0);
break;
case 22:
if (!strcmp(cstr, "compare:options:range:"))
return CheckNilArg(N, 0);
break;
case 23:
if (!strcmp(cstr, "caseInsensitiveCompare:"))
return CheckNilArg(N, 0);
Ted Kremenek
committed
case 29:
if (!strcmp(cstr, "compare:options:range:locale:"))
return CheckNilArg(N, 0);
break;
case 37:
if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
return CheckNilArg(N, 0);
break;
Ted Kremenek
committed
}
return false;
}