[cfe-commits] r116971 - in /cfe/trunk/lib/Checker: BasicObjCFoundationChecks.cpp BasicObjCFoundationChecks.h GRCoreEngine.cpp
Ted Kremenek
kremenek at apple.com
Wed Oct 20 16:38:56 PDT 2010
Author: kremenek
Date: Wed Oct 20 18:38:56 2010
New Revision: 116971
URL: http://llvm.org/viewvc/llvm-project?rev=116971&view=rev
Log:
Convert GRSimpleAPIChecks in BasicObjCFoundationChecks to be Checkers.
Modified:
cfe/trunk/lib/Checker/BasicObjCFoundationChecks.cpp
cfe/trunk/lib/Checker/BasicObjCFoundationChecks.h
cfe/trunk/lib/Checker/GRCoreEngine.cpp
Modified: cfe/trunk/lib/Checker/BasicObjCFoundationChecks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/BasicObjCFoundationChecks.cpp?rev=116971&r1=116970&r2=116971&view=diff
==============================================================================
--- cfe/trunk/lib/Checker/BasicObjCFoundationChecks.cpp (original)
+++ cfe/trunk/lib/Checker/BasicObjCFoundationChecks.cpp Wed Oct 20 18:38:56 2010
@@ -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,134 +69,82 @@
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;
+static bool isNSString(llvm::StringRef ClassName) {
+ return ClassName == "NSString" || ClassName == "NSMutableString";
+}
- bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
- bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
+static inline bool isNil(SVal X) {
+ return isa<loc::ConcreteInt>(X);
+}
- bool CheckNilArg(ExplodedNode* N, unsigned Arg);
+//===----------------------------------------------------------------------===//
+// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
+//===----------------------------------------------------------------------===//
+class NilArgChecker : public CheckerVisitor<NilArgChecker> {
+ APIMisuse *BT;
+ void AuditNSString(CheckerContext &C, const ObjCMessageExpr* ME);
+ void WarnNilArg(CheckerContext &C, const ObjCMessageExpr* ME, unsigned Arg);
public:
- BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
- : BT(0), BR(br), Ctx(ctx) {}
-
- bool Audit(ExplodedNode* N, GRStateManager&);
+ NilArgChecker() : BT(0) {}
+ static void *getTag() { static int x = 0; return &x; }
+ void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
+};
-private:
- void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
+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.";
-
- // 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");
+ << ME->getSelector().getAsString() << "' cannot be nil";
RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
R->addRange(ME->getArg(Arg)->getSourceRange());
- BR.EmitReport(R);
+ C.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());
-
+void NilArgChecker::PreVisitObjCMessageExpr(CheckerContext &C,
+ const ObjCMessageExpr *ME)
+{
const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
-
if (!ReceiverType)
- return false;
-
- if (isNSString(ReceiverType,
- ReceiverType->getDecl()->getIdentifier()->getName()))
- return AuditNSString(N, ME);
-
- return false;
-}
-
-static inline bool isNil(SVal X) {
- return isa<loc::ConcreteInt>(X);
-}
-
-//===----------------------------------------------------------------------===//
-// Error reporting.
-//===----------------------------------------------------------------------===//
-
-bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
- const ObjCMessageExpr* ME =
- cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
-
- const Expr * E = ME->getArg(Arg);
-
- if (isNil(N->getState()->getSVal(E))) {
- WarnNilArg(N, ME, Arg);
- return true;
+ 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);
+ }
}
-
- 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;
}
//===----------------------------------------------------------------------===//
@@ -193,27 +152,16 @@
//===----------------------------------------------------------------------===//
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 @@
}
#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 @@
// 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 @@
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 @@
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 @@
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 @@
EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
report->addRange(Arg->getSourceRange());
report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
-
C.EmitReport(report);
return;
}
@@ -502,20 +444,15 @@
//===----------------------------------------------------------------------===//
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 @@
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 @@
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 (!N)
- return;
-
- 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";
+ if (ExplodedNode *N = C.GenerateNode()) {
+ 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());
}
Modified: cfe/trunk/lib/Checker/BasicObjCFoundationChecks.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/BasicObjCFoundationChecks.h?rev=116971&r1=116970&r2=116971&view=diff
==============================================================================
--- cfe/trunk/lib/Checker/BasicObjCFoundationChecks.h (original)
+++ cfe/trunk/lib/Checker/BasicObjCFoundationChecks.h Wed Oct 20 18:38:56 2010
@@ -22,13 +22,6 @@
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);
Modified: cfe/trunk/lib/Checker/GRCoreEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/GRCoreEngine.cpp?rev=116971&r1=116970&r2=116971&view=diff
==============================================================================
--- cfe/trunk/lib/Checker/GRCoreEngine.cpp (original)
+++ cfe/trunk/lib/Checker/GRCoreEngine.cpp Wed Oct 20 18:38:56 2010
@@ -461,14 +461,8 @@
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;
- }
+ const GRState* PredState = GetState(Pred);
ExplodedNode* N = generateNode(S, St, Pred, K);
if (N) {
More information about the cfe-commits
mailing list