summaryrefslogtreecommitdiffstats
path: root/lib/StaticAnalyzer/Core
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Core')
-rw-r--r--lib/StaticAnalyzer/Core/CFRefCount.cpp142
-rw-r--r--lib/StaticAnalyzer/Core/Environment.cpp3
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp78
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp36
5 files changed, 205 insertions, 61 deletions
diff --git a/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Core/CFRefCount.cpp
index 0512e2f..bf53029 100644
--- a/lib/StaticAnalyzer/Core/CFRefCount.cpp
+++ b/lib/StaticAnalyzer/Core/CFRefCount.cpp
@@ -126,6 +126,7 @@ public:
/// ArgEffect is used to summarize a function/method call's effect on a
/// particular argument.
enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing,
+ DecRefBridgedTransfered,
DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape,
NewAutoreleasePool, SelfOwn, StopTracking };
@@ -148,7 +149,8 @@ namespace {
class RetEffect {
public:
enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol,
- NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias,
+ NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol,
+ ReceiverAlias,
OwnedWhenTrackedReceiver };
enum ObjKind { CF, ObjC, AnyObj };
@@ -195,7 +197,9 @@ public:
static RetEffect MakeGCNotOwned() {
return RetEffect(GCNotOwnedSymbol, ObjC);
}
-
+ static RetEffect MakeARCNotOwned() {
+ return RetEffect(ARCNotOwnedSymbol, ObjC);
+ }
static RetEffect MakeNoRet() {
return RetEffect(NoRet);
}
@@ -636,6 +640,9 @@ class RetainSummaryManager {
/// GCEnabled - Records whether or not the analyzed code runs in GC mode.
const bool GCEnabled;
+ /// Records whether or not the analyzed code runs in ARC mode.
+ const bool ARCEnabled;
+
/// FuncSummaries - A map from FunctionDecls to summaries.
FuncSummariesTy FuncSummaries;
@@ -788,14 +795,20 @@ private:
public:
- RetainSummaryManager(ASTContext& ctx, bool gcenabled)
+ RetainSummaryManager(ASTContext& ctx, bool gcenabled, bool usesARC)
: Ctx(ctx),
CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")),
- GCEnabled(gcenabled), AF(BPAlloc), ScratchArgs(AF.getEmptyMap()),
- ObjCAllocRetE(gcenabled ? RetEffect::MakeGCNotOwned()
- : RetEffect::MakeOwned(RetEffect::ObjC, true)),
- ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned()
- : RetEffect::MakeOwnedWhenTrackedReceiver()),
+ GCEnabled(gcenabled),
+ ARCEnabled(usesARC),
+ AF(BPAlloc), ScratchArgs(AF.getEmptyMap()),
+ ObjCAllocRetE(gcenabled
+ ? RetEffect::MakeGCNotOwned()
+ : (usesARC ? RetEffect::MakeARCNotOwned()
+ : RetEffect::MakeOwned(RetEffect::ObjC, true))),
+ ObjCInitRetE(gcenabled
+ ? RetEffect::MakeGCNotOwned()
+ : (usesARC ? RetEffect::MakeARCNotOwned()
+ : RetEffect::MakeOwnedWhenTrackedReceiver())),
DefaultSummary(AF.getEmptyMap() /* per-argument effects (none) */,
RetEffect::MakeNoRet() /* return effect */,
MayEscape, /* default argument effect */
@@ -871,6 +884,10 @@ public:
bool isGCEnabled() const { return GCEnabled; }
+ bool isARCEnabled() const { return ARCEnabled; }
+
+ bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; }
+
RetainSummary *copySummary(RetainSummary *OldSumm) {
RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
new (Summ) RetainSummary(*OldSumm);
@@ -1118,8 +1135,7 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) {
RetainSummary*
RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD,
StringRef FName) {
- if (FName.find("Create") != StringRef::npos ||
- FName.find("Copy") != StringRef::npos)
+ if (coreFoundation::followsCreateRule(FName))
return getCFSummaryCreateRule(FD);
return getCFSummaryGetRule(FD);
@@ -1189,7 +1205,8 @@ RetainSummaryManager::getInitMethodSummary(QualType RetTy) {
assert(ScratchArgs.isEmpty());
// 'init' methods conceptually return a newly allocated object and claim
// the receiver.
- if (cocoa::isCocoaObjectRef(RetTy) || cocoa::isCFObjectRef(RetTy))
+ if (cocoa::isCocoaObjectRef(RetTy) ||
+ coreFoundation::isCFObjectRef(RetTy))
return getPersistentSummary(ObjCInitRetE, DecRefMsg);
return getDefaultSummary();
@@ -1332,15 +1349,15 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD,
if (cocoa::isCocoaObjectRef(RetTy)) {
// EXPERIMENTAL: assume the Cocoa conventions for all objects returned
// by instance methods.
- RetEffect E = cocoa::followsFundamentalRule(S)
+ RetEffect E = cocoa::followsFundamentalRule(S, MD)
? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC);
return getPersistentSummary(E, ReceiverEff, MayEscape);
}
// Look for methods that return an owned core foundation object.
- if (cocoa::isCFObjectRef(RetTy)) {
- RetEffect E = cocoa::followsFundamentalRule(S)
+ if (coreFoundation::isCFObjectRef(RetTy)) {
+ RetEffect E = cocoa::followsFundamentalRule(S, MD)
? RetEffect::MakeOwned(RetEffect::CF, true)
: RetEffect::MakeNotOwned(RetEffect::CF);
@@ -1411,7 +1428,7 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S,
assert(ScratchArgs.isEmpty());
// "initXXX": pass-through for receiver.
- if (cocoa::deriveNamingConvention(S) == cocoa::InitRule)
+ if (cocoa::deriveNamingConvention(S, MD) == cocoa::InitRule)
Summ = getInitMethodSummary(RetTy);
else
Summ = getCommonMethodSummary(MD, S, RetTy);
@@ -1654,7 +1671,6 @@ public:
const char* nl, const char* sep);
};
-private:
typedef llvm::DenseMap<const ExplodedNode*, const RetainSummary*>
SummaryLogTy;
@@ -1691,7 +1707,7 @@ private:
public:
CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts)
- : Summaries(Ctx, gcenabled),
+ : Summaries(Ctx, gcenabled, (bool)lopts.ObjCAutoRefCount),
LOpts(lopts), useAfterRelease(0), releaseNotOwned(0),
deallocGC(0), deallocNotOwned(0),
leakWithinFunction(0), leakAtReturn(0), overAutorelease(0),
@@ -1706,6 +1722,8 @@ public:
}
bool isGCEnabled() const { return Summaries.isGCEnabled(); }
+ bool isARCorGCEnabled() const { return Summaries.isARCorGCEnabled(); }
+
const LangOptions& getLangOptions() const { return LOpts; }
const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const {
@@ -1907,7 +1925,7 @@ namespace {
CFRefBug(tf, "Method should return an owned object") {}
const char *getDescription() const {
- return "Object with +0 retain counts returned to caller where a +1 "
+ return "Object with a +0 retain count returned to caller where a +1 "
"(owning) retain count is expected";
}
};
@@ -2094,7 +2112,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) {
assert(CurrV.getObjKind() == RetEffect::CF);
- os << " "
+ os << ". "
"Core Foundation objects are not automatically garbage collected.";
}
}
@@ -2416,14 +2434,14 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,
if (RV->getKind() == RefVal::ErrorLeakReturned) {
// FIXME: Per comments in rdar://6320065, "create" only applies to CF
- // ojbects. Only "copy", "alloc", "retain" and "new" transfer ownership
+ // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
// to the caller for NS objects.
const Decl *D = &EndN->getCodeDecl();
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
os << " is returned from a method whose name ('"
<< MD->getSelector().getAsString()
<< "') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'."
- " This violates the naming convention rules "
+ " This violates the naming convention rules"
" given in the Memory Management Guide for Cocoa";
}
else {
@@ -2431,7 +2449,7 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,
os << " is return from a function whose name ('"
<< FD->getNameAsString()
<< "') does not contain 'Copy' or 'Create'. This violates the naming"
- " convention rules given the Memory Management Guide for Core "
+ " convention rules given the Memory Management Guide for Core"
" Foundation";
}
}
@@ -2777,6 +2795,7 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst,
}
case RetEffect::GCNotOwnedSymbol:
+ case RetEffect::ARCNotOwnedSymbol:
case RetEffect::NotOwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
SValBuilder &svalBuilder = Eng.getSValBuilder();
@@ -3103,8 +3122,8 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,
// In GC mode [... release] and [... retain] do nothing.
switch (E) {
default: break;
- case IncRefMsg: E = isGCEnabled() ? DoNothing : IncRef; break;
- case DecRefMsg: E = isGCEnabled() ? DoNothing : DecRef; break;
+ case IncRefMsg: E = isARCorGCEnabled() ? DoNothing : IncRef; break;
+ case DecRefMsg: E = isARCorGCEnabled() ? DoNothing : DecRef; break;
case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break;
case NewAutoreleasePool: E = isGCEnabled() ? DoNothing :
NewAutoreleasePool; break;
@@ -3118,9 +3137,13 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,
}
switch (E) {
- default:
- assert (false && "Unhandled CFRef transition.");
-
+ case DecRefMsg:
+ case IncRefMsg:
+ case MakeCollectable:
+ assert(false &&
+ "DecRefMsg/IncRefMsg/MakeCollectable already transformed");
+ return state;
+
case Dealloc:
// Any use of -dealloc in GC is *bad*.
if (isGCEnabled()) {
@@ -3193,6 +3216,7 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,
V = V ^ RefVal::NotOwned;
// Fall-through.
case DecRef:
+ case DecRefBridgedTransfered:
switch (V.getKind()) {
default:
// case 'RefVal::Released' handled above.
@@ -3200,7 +3224,9 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,
case RefVal::Owned:
assert(V.getCount() > 0);
- if (V.getCount() == 1) V = V ^ RefVal::Released;
+ if (V.getCount() == 1)
+ V = V ^ (E == DecRefBridgedTransfered ?
+ RefVal::NotOwned : RefVal::Released);
V = V - 1;
break;
@@ -3280,15 +3306,10 @@ CFRefCount::HandleAutoreleaseCounts(const GRState * state,
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
- os << "Object over-autoreleased: object was sent -autorelease";
+ os << "Object over-autoreleased: object was sent -autorelease ";
if (V.getAutoreleaseCount() > 1)
- os << V.getAutoreleaseCount() << " times";
- os << " but the object has ";
- if (V.getCount() == 0)
- os << "zero (locally visible)";
- else
- os << "+" << V.getCount();
- os << " retain counts";
+ os << V.getAutoreleaseCount() << " times ";
+ os << "but the object has a +" << V.getCount() << " retain count";
CFRefReport *report =
new CFRefReport(*static_cast<CFRefBug*>(overAutorelease),
@@ -3468,7 +3489,9 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst,
namespace {
class RetainReleaseChecker
- : public Checker< check::PostStmt<BlockExpr>, check::RegionChanges > {
+ : public Checker< check::PostStmt<BlockExpr>,
+ check::PostStmt<CastExpr>,
+ check::RegionChanges > {
public:
bool wantsRegionUpdate;
@@ -3476,6 +3499,9 @@ public:
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
+
+ void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
+
const GRState *checkRegionChanges(const GRState *state,
const StoreManager::InvalidatedSymbols *invalidated,
const MemRegion * const *begin,
@@ -3545,6 +3571,48 @@ void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE,
C.addTransition(state);
}
+void RetainReleaseChecker::checkPostStmt(const CastExpr *CE,
+ CheckerContext &C) const {
+ const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE);
+ if (!BE)
+ return;
+
+ ArgEffect AE = IncRef;
+
+ switch (BE->getBridgeKind()) {
+ case clang::OBC_Bridge:
+ // Do nothing.
+ return;
+ case clang::OBC_BridgeRetained:
+ AE = IncRef;
+ break;
+ case clang::OBC_BridgeTransfer:
+ AE = DecRefBridgedTransfered;
+ break;
+ }
+
+ const GRState *state = C.getState();
+ SymbolRef Sym = state->getSVal(CE).getAsLocSymbol();
+ if (!Sym)
+ return;
+ const RefVal* T = state->get<RefBindings>(Sym);
+ if (!T)
+ return;
+
+ // This is gross. Once the checker and CFRefCount are unified,
+ // this will go away.
+ CFRefCount &cf = static_cast<CFRefCount&>(C.getEngine().getTF());
+ RefVal::Kind hasErr = (RefVal::Kind) 0;
+ state = cf.Update(state, Sym, *T, AE, hasErr);
+
+ if (hasErr) {
+
+ return;
+ }
+
+ C.generateNode(state);
+}
+
//===----------------------------------------------------------------------===//
// Transfer function creation for external clients.
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp
index 48f126b..3961c7b 100644
--- a/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/lib/StaticAnalyzer/Core/Environment.cpp
@@ -83,6 +83,9 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder,
case Stmt::CXXBindTemporaryExprClass:
E = cast<CXXBindTemporaryExpr>(E)->getSubExpr();
continue;
+ case Stmt::MaterializeTemporaryExprClass:
+ E = cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr();
+ continue;
// Handle all other Stmt* using a lookup.
default:
break;
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index aed39eb..ffe5f0b 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -442,7 +442,8 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
}
switch (S->getStmtClass()) {
- // C++ stuff we don't support yet.
+ // C++ and ARC stuff we don't support yet.
+ case Expr::ObjCIndirectCopyRestoreExprClass:
case Stmt::CXXBindTemporaryExprClass:
case Stmt::CXXCatchStmtClass:
case Stmt::CXXDependentScopeMemberExprClass:
@@ -478,6 +479,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
// We don't handle default arguments either yet, but we can fake it
// for now by just skipping them.
+ case Stmt::SubstNonTypeTemplateParmExprClass:
case Stmt::CXXDefaultArgExprClass: {
Dst.Add(Pred);
break;
@@ -508,7 +510,10 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
break;
case Stmt::GNUNullExprClass: {
- MakeNode(Dst, S, Pred, GetState(Pred)->BindExpr(S, svalBuilder.makeNull()));
+ // GNU __null is a pointer-width integer, not an actual pointer.
+ const GRState *state = GetState(Pred);
+ state = state->BindExpr(S, svalBuilder.makeIntValWithPtrWidth(0, false));
+ MakeNode(Dst, S, Pred, state);
break;
}
@@ -520,14 +525,27 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
VisitObjCPropertyRefExpr(cast<ObjCPropertyRefExpr>(S), Pred, Dst);
break;
+ case Stmt::ImplicitValueInitExprClass: {
+ const GRState *state = GetState(Pred);
+ QualType ty = cast<ImplicitValueInitExpr>(S)->getType();
+ SVal val = svalBuilder.makeZeroVal(ty);
+ MakeNode(Dst, S, Pred, state->BindExpr(S, val));
+ break;
+ }
+
+ case Stmt::ExprWithCleanupsClass: {
+ Visit(cast<ExprWithCleanups>(S)->getSubExpr(), Pred, Dst);
+ break;
+ }
+
// Cases not handled yet; but will handle some day.
case Stmt::DesignatedInitExprClass:
case Stmt::ExtVectorElementExprClass:
case Stmt::ImaginaryLiteralClass:
- case Stmt::ImplicitValueInitExprClass:
case Stmt::ObjCAtCatchStmtClass:
case Stmt::ObjCAtFinallyStmtClass:
case Stmt::ObjCAtTryStmtClass:
+ case Stmt::ObjCAutoreleasePoolStmtClass:
case Stmt::ObjCEncodeExprClass:
case Stmt::ObjCIsaExprClass:
case Stmt::ObjCProtocolExprClass:
@@ -548,7 +566,6 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
case Stmt::IntegerLiteralClass:
case Stmt::CharacterLiteralClass:
case Stmt::CXXBoolLiteralExprClass:
- case Stmt::ExprWithCleanupsClass:
case Stmt::FloatingLiteralClass:
case Stmt::SizeOfPackExprClass:
case Stmt::CXXNullPtrLiteralExprClass:
@@ -668,12 +685,35 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
case Stmt::CXXDynamicCastExprClass:
case Stmt::CXXReinterpretCastExprClass:
case Stmt::CXXConstCastExprClass:
- case Stmt::CXXFunctionalCastExprClass: {
+ case Stmt::CXXFunctionalCastExprClass:
+ case Stmt::ObjCBridgedCastExprClass: {
const CastExpr* C = cast<CastExpr>(S);
- VisitCast(C, C->getSubExpr(), Pred, Dst);
+ // Handle the previsit checks.
+ ExplodedNodeSet dstPrevisit;
+ getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this);
+
+ // Handle the expression itself.
+ ExplodedNodeSet dstExpr;
+ for (ExplodedNodeSet::iterator i = dstPrevisit.begin(),
+ e = dstPrevisit.end(); i != e ; ++i) {
+ VisitCast(C, C->getSubExpr(), *i, dstExpr);
+ }
+
+ // Handle the postvisit checks.
+ getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this);
break;
}
+ case Expr::MaterializeTemporaryExprClass: {
+ const MaterializeTemporaryExpr *Materialize
+ = cast<MaterializeTemporaryExpr>(S);
+ if (!Materialize->getType()->isRecordType())
+ CreateCXXTemporaryObject(Materialize->GetTemporaryExpr(), Pred, Dst);
+ else
+ Visit(Materialize->GetTemporaryExpr(), Pred, Dst);
+ break;
+ }
+
case Stmt::InitListExprClass:
VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst);
break;
@@ -2148,10 +2188,19 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
Pred = *I;
switch (CastE->getCastKind()) {
+ case CK_LValueToRValue:
+ assert(false && "LValueToRValue casts handled earlier.");
+ case CK_GetObjCProperty:
+ assert(false && "GetObjCProperty casts handled earlier.");
case CK_ToVoid:
Dst.Add(Pred);
continue;
- case CK_LValueToRValue:
+ // The analyzer doesn't do anything special with these casts,
+ // since it understands retain/release semantics already.
+ case CK_ObjCProduceObject:
+ case CK_ObjCConsumeObject:
+ case CK_ObjCReclaimReturnedObject: // Fall-through.
+ // True no-ops.
case CK_NoOp:
case CK_FunctionToPointerDecay: {
// Copy the SVal of Ex to CastE.
@@ -2161,7 +2210,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
MakeNode(Dst, CastE, Pred, state);
continue;
}
- case CK_GetObjCProperty:
case CK_Dependent:
case CK_ArrayToPointerDecay:
case CK_BitCast:
@@ -2273,17 +2321,9 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
// time a function is called those values may not be current.
ExplodedNodeSet Tmp;
- if (InitEx) {
- if (VD->getType()->isReferenceType() && !InitEx->isLValue()) {
- // If the initializer is C++ record type, it should already has a
- // temp object.
- if (!InitEx->getType()->isRecordType())
- CreateCXXTemporaryObject(InitEx, Pred, Tmp);
- else
- Tmp.Add(Pred);
- } else
- Visit(InitEx, Pred, Tmp);
- } else
+ if (InitEx)
+ Visit(InitEx, Pred, Tmp);
+ else
Tmp.Add(Pred);
ExplodedNodeSet Tmp2;
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index d0d8f60..23dd641 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1377,7 +1377,12 @@ StoreRef RegionStoreManager::setImplicitDefaultValue(Store store,
V = svalBuilder.makeZeroVal(Ctx.IntTy);
}
else {
- return StoreRef(store, *this);
+ // We can't represent values of this type, but we still need to set a value
+ // to record that the region has been initialized.
+ // If this assertion ever fires, a new case should be added above -- we
+ // should know how to default-initialize any value we can symbolicate.
+ assert(!SymbolManager::canSymbolicate(T) && "This type is representable");
+ V = UnknownVal();
}
return StoreRef(addBinding(B, R, BindingKey::Default,
diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 197442b..80c18a3 100644
--- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -415,6 +415,24 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,
case nonloc::ConcreteIntKind: {
const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs);
+ // Is the RHS a symbol we can simplify?
+ // FIXME: This was mostly copy/pasted from the LHS-is-a-symbol case.
+ if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) {
+ SymbolRef RSym = srhs->getSymbol();
+ if (RSym->getType(Context)->isIntegerType()) {
+ if (const llvm::APSInt *Constant = state->getSymVal(RSym)) {
+ // The symbol evaluates to a constant.
+ const llvm::APSInt *rhs_I;
+ if (BinaryOperator::isRelationalOp(op))
+ rhs_I = &BasicVals.Convert(lhsInt.getValue(), *Constant);
+ else
+ rhs_I = &BasicVals.Convert(resultTy, *Constant);
+
+ rhs = nonloc::ConcreteInt(*rhs_I);
+ }
+ }
+ }
+
if (isa<nonloc::ConcreteInt>(rhs)) {
return lhsInt.evalBinOp(*this, op, cast<nonloc::ConcreteInt>(rhs));
} else {
@@ -461,13 +479,22 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,
case nonloc::SymbolValKind: {
nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs);
SymbolRef Sym = slhs->getSymbol();
+ QualType lhsType = Sym->getType(Context);
+
+ // The conversion type is usually the result type, but not in the case
+ // of relational expressions.
+ QualType conversionType = resultTy;
+ if (BinaryOperator::isRelationalOp(op))
+ conversionType = lhsType;
+
// Does the symbol simplify to a constant? If so, "fold" the constant
// by setting 'lhs' to a ConcreteInt and try again.
- if (Sym->getType(Context)->isIntegerType())
+ if (lhsType->isIntegerType())
if (const llvm::APSInt *Constant = state->getSymVal(Sym)) {
// The symbol evaluates to a constant. If necessary, promote the
// folded constant (LHS) to the result type.
- const llvm::APSInt &lhs_I = BasicVals.Convert(resultTy, *Constant);
+ const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType,
+ *Constant);
lhs = nonloc::ConcreteInt(lhs_I);
// Also promote the RHS (if necessary).
@@ -479,7 +506,7 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,
// Other operators: do an implicit conversion. This shouldn't be
// necessary once we support truncation/extension of symbolic values.
if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){
- rhs = nonloc::ConcreteInt(BasicVals.Convert(resultTy,
+ rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType,
rhs_I->getValue()));
}
@@ -492,7 +519,8 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state,
if (RSym->getType(Context)->isIntegerType()) {
if (const llvm::APSInt *Constant = state->getSymVal(RSym)) {
// The symbol evaluates to a constant.
- const llvm::APSInt &rhs_I = BasicVals.Convert(resultTy, *Constant);
+ const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType,
+ *Constant);
rhs = nonloc::ConcreteInt(rhs_I);
}
}
OpenPOWER on IntegriCloud