forked from OSchip/llvm-project
Convert GRSimpleAPIChecks in BasicObjCFoundationChecks to be Checkers.
llvm-svn: 116971
This commit is contained in:
parent
a65f914bb0
commit
bd2c800882
|
@ -16,7 +16,7 @@
|
|||
#include "BasicObjCFoundationChecks.h"
|
||||
|
||||
#include "clang/Checker/PathSensitive/ExplodedGraph.h"
|
||||
#include "clang/Checker/PathSensitive/GRSimpleAPICheck.h"
|
||||
#include "clang/Checker/PathSensitive/CheckerVisitor.h"
|
||||
#include "clang/Checker/PathSensitive/GRExprEngine.h"
|
||||
#include "clang/Checker/PathSensitive/GRState.h"
|
||||
#include "clang/Checker/BugReporter/BugType.h"
|
||||
|
@ -30,25 +30,36 @@
|
|||
|
||||
using namespace clang;
|
||||
|
||||
namespace {
|
||||
class APIMisuse : public BugType {
|
||||
public:
|
||||
APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Utility functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
|
||||
QualType T;
|
||||
switch (ME->getReceiverKind()) {
|
||||
case ObjCMessageExpr::Instance:
|
||||
T = ME->getInstanceReceiver()->getType();
|
||||
break;
|
||||
|
||||
case ObjCMessageExpr::SuperInstance:
|
||||
T = ME->getSuperType();
|
||||
break;
|
||||
|
||||
case ObjCMessageExpr::Class:
|
||||
case ObjCMessageExpr::SuperClass:
|
||||
return 0;
|
||||
case ObjCMessageExpr::Instance:
|
||||
T = ME->getInstanceReceiver()->getType();
|
||||
break;
|
||||
|
||||
case ObjCMessageExpr::SuperInstance:
|
||||
T = ME->getSuperType();
|
||||
break;
|
||||
|
||||
case ObjCMessageExpr::Class:
|
||||
case ObjCMessageExpr::SuperClass:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>())
|
||||
return PT->getInterfaceType();
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -58,72 +69,8 @@ static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class APIMisuse : public BugType {
|
||||
public:
|
||||
APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
|
||||
};
|
||||
|
||||
class BasicObjCFoundationChecks : public GRSimpleAPICheck {
|
||||
APIMisuse *BT;
|
||||
BugReporter& BR;
|
||||
ASTContext &Ctx;
|
||||
|
||||
bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
|
||||
bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
|
||||
|
||||
bool CheckNilArg(ExplodedNode* N, unsigned Arg);
|
||||
|
||||
public:
|
||||
BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
|
||||
: BT(0), BR(br), Ctx(ctx) {}
|
||||
|
||||
bool Audit(ExplodedNode* N, GRStateManager&);
|
||||
|
||||
private:
|
||||
void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
|
||||
std::string sbuf;
|
||||
llvm::raw_string_ostream os(sbuf);
|
||||
os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
|
||||
<< ME->getSelector().getAsString() << "' cannot be nil.";
|
||||
|
||||
// Lazily create the BugType object for NilArg. This will be owned
|
||||
// by the BugReporter object 'BR' once we call BR.EmitWarning.
|
||||
if (!BT) BT = new APIMisuse("nil argument");
|
||||
|
||||
RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
|
||||
R->addRange(ME->getArg(Arg)->getSourceRange());
|
||||
BR.EmitReport(R);
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
GRSimpleAPICheck*
|
||||
clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
|
||||
return new BasicObjCFoundationChecks(Ctx, BR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
|
||||
GRStateManager&) {
|
||||
|
||||
const ObjCMessageExpr* ME =
|
||||
cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
|
||||
|
||||
const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
|
||||
|
||||
if (!ReceiverType)
|
||||
return false;
|
||||
|
||||
if (isNSString(ReceiverType,
|
||||
ReceiverType->getDecl()->getIdentifier()->getName()))
|
||||
return AuditNSString(N, ME);
|
||||
|
||||
return false;
|
||||
static bool isNSString(llvm::StringRef ClassName) {
|
||||
return ClassName == "NSString" || ClassName == "NSMutableString";
|
||||
}
|
||||
|
||||
static inline bool isNil(SVal X) {
|
||||
|
@ -131,61 +78,73 @@ static inline bool isNil(SVal X) {
|
|||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Error reporting.
|
||||
// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
|
||||
const ObjCMessageExpr* ME =
|
||||
cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
|
||||
class NilArgChecker : public CheckerVisitor<NilArgChecker> {
|
||||
APIMisuse *BT;
|
||||
void AuditNSString(CheckerContext &C, const ObjCMessageExpr* ME);
|
||||
void WarnNilArg(CheckerContext &C, const ObjCMessageExpr* ME, unsigned Arg);
|
||||
public:
|
||||
NilArgChecker() : BT(0) {}
|
||||
static void *getTag() { static int x = 0; return &x; }
|
||||
void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
|
||||
};
|
||||
|
||||
const Expr * E = ME->getArg(Arg);
|
||||
void NilArgChecker::WarnNilArg(CheckerContext &C,
|
||||
const clang::ObjCMessageExpr *ME,
|
||||
unsigned int Arg)
|
||||
{
|
||||
if (!BT)
|
||||
BT = new APIMisuse("nil argument");
|
||||
|
||||
if (ExplodedNode *N = C.GenerateSink()) {
|
||||
llvm::SmallString<128> sbuf;
|
||||
llvm::raw_svector_ostream os(sbuf);
|
||||
os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
|
||||
<< ME->getSelector().getAsString() << "' cannot be nil";
|
||||
|
||||
if (isNil(N->getState()->getSVal(E))) {
|
||||
WarnNilArg(N, ME, Arg);
|
||||
return true;
|
||||
RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
|
||||
R->addRange(ME->getArg(Arg)->getSourceRange());
|
||||
C.EmitReport(R);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// NSString checking.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
|
||||
llvm::StringRef ClassName) {
|
||||
return ClassName == "NSString" || ClassName == "NSMutableString";
|
||||
}
|
||||
|
||||
bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
|
||||
const 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 NameStr = S.getAsString();
|
||||
llvm::StringRef Name(NameStr);
|
||||
assert(!Name.empty());
|
||||
|
||||
// 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 (Name == "caseInsensitiveCompare:" ||
|
||||
Name == "compare:" ||
|
||||
Name == "compare:options:" ||
|
||||
Name == "compare:options:range:" ||
|
||||
Name == "compare:options:range:locale:" ||
|
||||
Name == "componentsSeparatedByCharactersInSet:" ||
|
||||
Name == "initWithFormat:")
|
||||
return CheckNilArg(N, 0);
|
||||
|
||||
return false;
|
||||
void NilArgChecker::PreVisitObjCMessageExpr(CheckerContext &C,
|
||||
const ObjCMessageExpr *ME)
|
||||
{
|
||||
const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
|
||||
if (!ReceiverType)
|
||||
return;
|
||||
|
||||
if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) {
|
||||
Selector S = ME->getSelector();
|
||||
|
||||
if (S.isUnarySelector())
|
||||
return;
|
||||
|
||||
// FIXME: This is going to be really slow doing these checks with
|
||||
// lexical comparisons.
|
||||
|
||||
std::string NameStr = S.getAsString();
|
||||
llvm::StringRef Name(NameStr);
|
||||
assert(!Name.empty());
|
||||
|
||||
// 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 (Name == "caseInsensitiveCompare:" ||
|
||||
Name == "compare:" ||
|
||||
Name == "compare:options:" ||
|
||||
Name == "compare:options:range:" ||
|
||||
Name == "compare:options:range:locale:" ||
|
||||
Name == "componentsSeparatedByCharactersInSet:" ||
|
||||
Name == "initWithFormat:") {
|
||||
if (isNil(C.getState()->getSVal(ME->getArg(0))))
|
||||
WarnNilArg(C, ME, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -193,27 +152,16 @@ bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
||||
class AuditCFNumberCreate : public GRSimpleAPICheck {
|
||||
class CFNumberCreateChecker : public CheckerVisitor<CFNumberCreateChecker> {
|
||||
APIMisuse* BT;
|
||||
|
||||
// FIXME: Either this should be refactored into GRSimpleAPICheck, or
|
||||
// it should always be passed with a call to Audit. The latter
|
||||
// approach makes this class more stateless.
|
||||
ASTContext& Ctx;
|
||||
IdentifierInfo* II;
|
||||
BugReporter& BR;
|
||||
|
||||
public:
|
||||
AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
|
||||
: BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
|
||||
|
||||
~AuditCFNumberCreate() {}
|
||||
|
||||
bool Audit(ExplodedNode* N, GRStateManager&);
|
||||
|
||||
CFNumberCreateChecker() : BT(0), II(0) {}
|
||||
~CFNumberCreateChecker() {}
|
||||
static void *getTag() { static int x = 0; return &x; }
|
||||
void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
|
||||
private:
|
||||
void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
|
||||
void EmitError(const TypedRegion* R, const Expr* Ex,
|
||||
uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
@ -311,49 +259,54 @@ static const char* GetCFNumberTypeStr(uint64_t i) {
|
|||
}
|
||||
#endif
|
||||
|
||||
bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
|
||||
const CallExpr* CE =
|
||||
cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
|
||||
void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C,
|
||||
const CallExpr *CE)
|
||||
{
|
||||
const Expr* Callee = CE->getCallee();
|
||||
SVal CallV = N->getState()->getSVal(Callee);
|
||||
const GRState *state = C.getState();
|
||||
SVal CallV = state->getSVal(Callee);
|
||||
const FunctionDecl* FD = CallV.getAsFunctionDecl();
|
||||
|
||||
if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
|
||||
return false;
|
||||
if (!FD)
|
||||
return;
|
||||
|
||||
ASTContext &Ctx = C.getASTContext();
|
||||
if (!II)
|
||||
II = &Ctx.Idents.get("CFNumberCreate");
|
||||
|
||||
if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
|
||||
return;
|
||||
|
||||
// Get the value of the "theType" argument.
|
||||
SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
|
||||
SVal TheTypeVal = state->getSVal(CE->getArg(1));
|
||||
|
||||
// FIXME: We really should allow ranges of valid theType values, and
|
||||
// bifurcate the state appropriately.
|
||||
// FIXME: We really should allow ranges of valid theType values, and
|
||||
// bifurcate the state appropriately.
|
||||
nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
|
||||
|
||||
if (!V)
|
||||
return false;
|
||||
return;
|
||||
|
||||
uint64_t NumberKind = V->getValue().getLimitedValue();
|
||||
Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
|
||||
|
||||
// FIXME: In some cases we can emit an error.
|
||||
if (!TargetSize.isKnown())
|
||||
return false;
|
||||
return;
|
||||
|
||||
// Look at the value of the integer being passed by reference. Essentially
|
||||
// we want to catch cases where the value passed in is not equal to the
|
||||
// size of the type being created.
|
||||
SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
|
||||
SVal TheValueExpr = state->getSVal(CE->getArg(2));
|
||||
|
||||
// FIXME: Eventually we should handle arbitrary locations. We can do this
|
||||
// by having an enhanced memory model that does low-level typing.
|
||||
loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
|
||||
|
||||
if (!LV)
|
||||
return false;
|
||||
return;
|
||||
|
||||
const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
|
||||
|
||||
if (!R)
|
||||
return false;
|
||||
return;
|
||||
|
||||
QualType T = Ctx.getCanonicalType(R->getValueType());
|
||||
|
||||
|
@ -361,54 +314,45 @@ bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
|
|||
// People can do weird stuff with pointers.
|
||||
|
||||
if (!T->isIntegerType())
|
||||
return false;
|
||||
return;
|
||||
|
||||
uint64_t SourceSize = Ctx.getTypeSize(T);
|
||||
|
||||
// CHECK: is SourceSize == TargetSize
|
||||
|
||||
if (SourceSize == TargetSize)
|
||||
return false;
|
||||
|
||||
AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
|
||||
return;
|
||||
|
||||
// Generate an error. Only generate a sink if 'SourceSize < TargetSize';
|
||||
// otherwise generate a regular node.
|
||||
//
|
||||
// FIXME: We can actually create an abstract "CFNumber" object that has
|
||||
// the bits initialized to the provided values.
|
||||
return SourceSize < TargetSize;
|
||||
}
|
||||
//
|
||||
if (ExplodedNode *N = SourceSize < TargetSize ? C.GenerateSink()
|
||||
: C.GenerateNode()) {
|
||||
llvm::SmallString<128> sbuf;
|
||||
llvm::raw_svector_ostream os(sbuf);
|
||||
|
||||
os << (SourceSize == 8 ? "An " : "A ")
|
||||
<< SourceSize << " bit integer is used to initialize a CFNumber "
|
||||
"object that represents "
|
||||
<< (TargetSize == 8 ? "an " : "a ")
|
||||
<< TargetSize << " bit integer. ";
|
||||
|
||||
if (SourceSize < TargetSize)
|
||||
os << (TargetSize - SourceSize)
|
||||
<< " bits of the CFNumber value will be garbage." ;
|
||||
else
|
||||
os << (SourceSize - TargetSize)
|
||||
<< " bits of the input integer will be lost.";
|
||||
|
||||
void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
|
||||
ExplodedNode *N,
|
||||
uint64_t SourceSize, uint64_t TargetSize,
|
||||
uint64_t NumberKind) {
|
||||
|
||||
std::string sbuf;
|
||||
llvm::raw_string_ostream os(sbuf);
|
||||
|
||||
os << (SourceSize == 8 ? "An " : "A ")
|
||||
<< SourceSize << " bit integer is used to initialize a CFNumber "
|
||||
"object that represents "
|
||||
<< (TargetSize == 8 ? "an " : "a ")
|
||||
<< TargetSize << " bit integer. ";
|
||||
|
||||
if (SourceSize < TargetSize)
|
||||
os << (TargetSize - SourceSize)
|
||||
<< " bits of the CFNumber value will be garbage." ;
|
||||
else
|
||||
os << (SourceSize - TargetSize)
|
||||
<< " bits of the input integer will be lost.";
|
||||
|
||||
// Lazily create the BugType object. This will be owned
|
||||
// by the BugReporter object 'BR' once we call BR.EmitWarning.
|
||||
if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
|
||||
RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
|
||||
report->addRange(Ex->getSourceRange());
|
||||
BR.EmitReport(report);
|
||||
}
|
||||
|
||||
GRSimpleAPICheck*
|
||||
clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
|
||||
return new AuditCFNumberCreate(Ctx, BR);
|
||||
if (!BT)
|
||||
BT = new APIMisuse("Bad use of CFNumberCreate");
|
||||
|
||||
RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
|
||||
report->addRange(CE->getArg(2)->getSourceRange());
|
||||
C.EmitReport(report);
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -419,14 +363,9 @@ namespace {
|
|||
class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> {
|
||||
APIMisuse *BT;
|
||||
IdentifierInfo *Retain, *Release;
|
||||
|
||||
public:
|
||||
CFRetainReleaseChecker(ASTContext& Ctx): BT(NULL),
|
||||
Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease"))
|
||||
{}
|
||||
|
||||
CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {}
|
||||
static void *getTag() { static int x = 0; return &x; }
|
||||
|
||||
void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE);
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
@ -445,6 +384,13 @@ void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
|
|||
|
||||
if (!FD)
|
||||
return;
|
||||
|
||||
if (!BT) {
|
||||
ASTContext &Ctx = C.getASTContext();
|
||||
Retain = &Ctx.Idents.get("CFRetain");
|
||||
Release = &Ctx.Idents.get("CFRelease");
|
||||
BT = new APIMisuse("null passed to CFRetain/CFRelease");
|
||||
}
|
||||
|
||||
// Check if we called CFRetain/CFRelease.
|
||||
const IdentifierInfo *FuncII = FD->getIdentifier();
|
||||
|
@ -478,9 +424,6 @@ void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
|
|||
if (!N)
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT = new APIMisuse("null passed to CFRetain/CFRelease");
|
||||
|
||||
const char *description = (FuncII == Retain)
|
||||
? "Null pointer argument in call to CFRetain"
|
||||
: "Null pointer argument in call to CFRelease";
|
||||
|
@ -488,7 +431,6 @@ void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
|
|||
EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
|
||||
report->addRange(Arg->getSourceRange());
|
||||
report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
|
||||
|
||||
C.EmitReport(report);
|
||||
return;
|
||||
}
|
||||
|
@ -502,20 +444,15 @@ void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
class ClassReleaseChecker :
|
||||
public CheckerVisitor<ClassReleaseChecker> {
|
||||
class ClassReleaseChecker : public CheckerVisitor<ClassReleaseChecker> {
|
||||
Selector releaseS;
|
||||
Selector retainS;
|
||||
Selector autoreleaseS;
|
||||
Selector drainS;
|
||||
BugType *BT;
|
||||
public:
|
||||
ClassReleaseChecker(ASTContext &Ctx)
|
||||
: releaseS(GetNullarySelector("release", Ctx)),
|
||||
retainS(GetNullarySelector("retain", Ctx)),
|
||||
autoreleaseS(GetNullarySelector("autorelease", Ctx)),
|
||||
drainS(GetNullarySelector("drain", Ctx)),
|
||||
BT(0) {}
|
||||
ClassReleaseChecker()
|
||||
: BT(0) {}
|
||||
|
||||
static void *getTag() { static int x = 0; return &x; }
|
||||
|
||||
|
@ -525,16 +462,27 @@ public:
|
|||
|
||||
void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
|
||||
const ObjCMessageExpr *ME) {
|
||||
|
||||
if (!BT) {
|
||||
BT = new APIMisuse("message incorrectly sent to class instead of class "
|
||||
"instance");
|
||||
|
||||
ASTContext &Ctx = C.getASTContext();
|
||||
releaseS = GetNullarySelector("release", Ctx);
|
||||
retainS = GetNullarySelector("retain", Ctx);
|
||||
autoreleaseS = GetNullarySelector("autorelease", Ctx);
|
||||
drainS = GetNullarySelector("drain", Ctx);
|
||||
}
|
||||
|
||||
ObjCInterfaceDecl *Class = 0;
|
||||
|
||||
switch (ME->getReceiverKind()) {
|
||||
case ObjCMessageExpr::Class:
|
||||
Class = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface();
|
||||
break;
|
||||
|
||||
case ObjCMessageExpr::SuperClass:
|
||||
Class = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface();
|
||||
break;
|
||||
|
||||
case ObjCMessageExpr::Instance:
|
||||
case ObjCMessageExpr::SuperInstance:
|
||||
return;
|
||||
|
@ -544,42 +492,29 @@ void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
|
|||
if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
|
||||
return;
|
||||
|
||||
if (!BT)
|
||||
BT = new APIMisuse("message incorrectly sent to class instead of class "
|
||||
"instance");
|
||||
|
||||
ExplodedNode *N = C.GenerateNode();
|
||||
if (ExplodedNode *N = C.GenerateNode()) {
|
||||
llvm::SmallString<200> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
|
||||
if (!N)
|
||||
return;
|
||||
os << "The '" << S.getAsString() << "' message should be sent to instances "
|
||||
"of class '" << Class->getName()
|
||||
<< "' and not the class directly";
|
||||
|
||||
llvm::SmallString<200> buf;
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
|
||||
os << "The '" << S.getAsString() << "' message should be sent to instances "
|
||||
"of class '" << Class->getName()
|
||||
<< "' and not the class directly";
|
||||
|
||||
RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
|
||||
report->addRange(ME->getSourceRange());
|
||||
C.EmitReport(report);
|
||||
RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
|
||||
report->addRange(ME->getSourceRange());
|
||||
C.EmitReport(report);
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Check registration.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
|
||||
ASTContext& Ctx = Eng.getContext();
|
||||
BugReporter &BR = Eng.getBugReporter();
|
||||
|
||||
Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
|
||||
Stmt::ObjCMessageExprClass);
|
||||
Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
|
||||
|
||||
RegisterNSErrorChecks(BR, Eng, D);
|
||||
Eng.registerCheck(new NilArgChecker());
|
||||
Eng.registerCheck(new CFNumberCreateChecker());
|
||||
RegisterNSErrorChecks(Eng.getBugReporter(), Eng, D);
|
||||
RegisterNSAutoreleasePoolChecks(Eng);
|
||||
|
||||
Eng.registerCheck(new CFRetainReleaseChecker(Ctx));
|
||||
Eng.registerCheck(new ClassReleaseChecker(Ctx));
|
||||
Eng.registerCheck(new CFRetainReleaseChecker());
|
||||
Eng.registerCheck(new ClassReleaseChecker());
|
||||
}
|
||||
|
|
|
@ -22,13 +22,6 @@ class ASTContext;
|
|||
class BugReporter;
|
||||
class Decl;
|
||||
class GRExprEngine;
|
||||
class GRSimpleAPICheck;
|
||||
|
||||
GRSimpleAPICheck *CreateBasicObjCFoundationChecks(ASTContext& Ctx,
|
||||
BugReporter& BR);
|
||||
|
||||
GRSimpleAPICheck *CreateAuditCFNumberCreate(ASTContext& Ctx,
|
||||
BugReporter& BR);
|
||||
|
||||
void RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng, const Decl &D);
|
||||
void RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng);
|
||||
|
|
|
@ -461,14 +461,8 @@ void GRStmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) {
|
|||
ExplodedNode* GRStmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S,
|
||||
ExplodedNode* Pred, const GRState* St,
|
||||
ProgramPoint::Kind K) {
|
||||
|
||||
const GRState* PredState = GetState(Pred);
|
||||
|
||||
// If the state hasn't changed, don't generate a new node.
|
||||
if (!BuildSinks && St == PredState && Auditor == 0) {
|
||||
Dst.Add(Pred);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ExplodedNode* N = generateNode(S, St, Pred, K);
|
||||
|
||||
if (N) {
|
||||
|
|
Loading…
Reference in New Issue