[analyzer] Replace isIntegerType() with isIntegerOrEnumerationType().
Previously, the analyzer used isIntegerType() everywhere, which uses the C definition of "integer". The C++ predicate with the same behavior is isIntegerOrUnscopedEnumerationType(). However, the analyzer is /really/ using this to ask if it's some sort of "integrally representable" type, i.e. it should include C++11 scoped enumerations as well. hasIntegerRepresentation() sounds like the right predicate, but that includes vectors, which the analyzer represents by its elements. This commit audits all uses of isIntegerType() and replaces them with the general isIntegerOrEnumerationType(), except in some specific cases where it makes sense to exclude scoped enumerations, or any enumerations. These cases now use isIntegerOrUnscopedEnumerationType() and getAs<BuiltinType>() plus BuiltinType::isInteger(). isIntegerType() is hereby banned in the analyzer - lib/StaticAnalysis and include/clang/StaticAnalysis. :-) Fixes real assertion failures. PR15703 / <rdar://problem/12350701> llvm-svn: 179081
This commit is contained in:
@@ -93,7 +93,7 @@ public:
|
|||||||
|
|
||||||
/// Returns the type of the APSInt used to store values of the given QualType.
|
/// Returns the type of the APSInt used to store values of the given QualType.
|
||||||
APSIntType getAPSIntType(QualType T) const {
|
APSIntType getAPSIntType(QualType T) const {
|
||||||
assert(T->isIntegerType() || Loc::isLocType(T));
|
assert(T->isIntegralOrEnumerationType() || Loc::isLocType(T));
|
||||||
return APSIntType(Ctx.getTypeSize(T),
|
return APSIntType(Ctx.getTypeSize(T),
|
||||||
!T->isSignedIntegerOrEnumerationType());
|
!T->isSignedIntegerOrEnumerationType());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -711,7 +711,8 @@ ProgramState::getSValAsScalarOrLoc(const Stmt *S,
|
|||||||
const LocationContext *LCtx) const {
|
const LocationContext *LCtx) const {
|
||||||
if (const Expr *Ex = dyn_cast<Expr>(S)) {
|
if (const Expr *Ex = dyn_cast<Expr>(S)) {
|
||||||
QualType T = Ex->getType();
|
QualType T = Ex->getType();
|
||||||
if (Ex->isGLValue() || Loc::isLocType(T) || T->isIntegerType())
|
if (Ex->isGLValue() || Loc::isLocType(T) ||
|
||||||
|
T->isIntegralOrEnumerationType())
|
||||||
return getSVal(S, LCtx);
|
return getSVal(S, LCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ public:
|
|||||||
// FIXME: Remove the second disjunct when we support symbolic
|
// FIXME: Remove the second disjunct when we support symbolic
|
||||||
// truncation/extension.
|
// truncation/extension.
|
||||||
return (Context.getCanonicalType(Ty1) == Context.getCanonicalType(Ty2) ||
|
return (Context.getCanonicalType(Ty1) == Context.getCanonicalType(Ty2) ||
|
||||||
(Ty1->isIntegerType() && Ty2->isIntegerType()));
|
(Ty1->isIntegralOrEnumerationType() &&
|
||||||
|
Ty2->isIntegralOrEnumerationType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
SVal evalCast(SVal val, QualType castTy, QualType originalType);
|
SVal evalCast(SVal val, QualType castTy, QualType originalType);
|
||||||
|
|||||||
@@ -388,7 +388,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
|
|||||||
// FIXME: If the pointee isn't an integer type, should we flag a warning?
|
// FIXME: If the pointee isn't an integer type, should we flag a warning?
|
||||||
// People can do weird stuff with pointers.
|
// People can do weird stuff with pointers.
|
||||||
|
|
||||||
if (!T->isIntegerType())
|
if (!T->isIntegralOrEnumerationType())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint64_t SourceSize = Ctx.getTypeSize(T);
|
uint64_t SourceSize = Ctx.getTypeSize(T);
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Verify the first argument type is integer.
|
// Verify the first argument type is integer.
|
||||||
if (!FPT->getArgType(0)->isIntegerType())
|
if (!FPT->getArgType(0)->isIntegralOrUnscopedEnumerationType())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Verify the second argument type is char*.
|
// Verify the second argument type is char*.
|
||||||
@@ -602,7 +602,7 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
|
|||||||
if (!PT)
|
if (!PT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (! PT->getPointeeType()->isIntegerType())
|
if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (FTP->getNumArgs() != 0)
|
else if (FTP->getNumArgs() != 0)
|
||||||
@@ -725,7 +725,7 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
|
|||||||
|
|
||||||
// The arguments must be integers.
|
// The arguments must be integers.
|
||||||
for (unsigned i = 0; i < FTP->getNumArgs(); i++)
|
for (unsigned i = 0; i < FTP->getNumArgs(); i++)
|
||||||
if (! FTP->getArgType(i)->isIntegerType())
|
if (! FTP->getArgType(i)->isIntegralOrUnscopedEnumerationType())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Issue a warning.
|
// Issue a warning.
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ public:
|
|||||||
|
|
||||||
for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(),
|
for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(),
|
||||||
ae = i->AllocCall->arg_end(); ai != ae; ++ai) {
|
ae = i->AllocCall->arg_end(); ai != ae; ++ai) {
|
||||||
if (!(*ai)->getType()->isIntegerType())
|
if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
SizeofFinder SFinder;
|
SizeofFinder SFinder;
|
||||||
|
|||||||
@@ -1351,7 +1351,7 @@ ConditionBRVisitor::VisitConditionVariable(StringRef LhsString,
|
|||||||
Out << (tookTrue ? "not nil" : "nil");
|
Out << (tookTrue ? "not nil" : "nil");
|
||||||
else if (Ty->isBooleanType())
|
else if (Ty->isBooleanType())
|
||||||
Out << (tookTrue ? "true" : "false");
|
Out << (tookTrue ? "true" : "false");
|
||||||
else if (Ty->isIntegerType())
|
else if (Ty->isIntegralOrEnumerationType())
|
||||||
Out << (tookTrue ? "non-zero" : "zero");
|
Out << (tookTrue ? "non-zero" : "zero");
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -106,7 +106,8 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
|
|||||||
|
|
||||||
const ParmVarDecl *PD = FD->getParamDecl(0);
|
const ParmVarDecl *PD = FD->getParamDecl(0);
|
||||||
QualType T = PD->getType();
|
QualType T = PD->getType();
|
||||||
if (!T->isIntegerType())
|
const BuiltinType *BT = dyn_cast<BuiltinType>(T);
|
||||||
|
if (!BT || !BT->isInteger())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
const MemRegion *R = state->getRegion(PD, InitLoc);
|
const MemRegion *R = state->getRegion(PD, InitLoc);
|
||||||
@@ -1235,7 +1236,7 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr,
|
|||||||
while (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) {
|
while (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) {
|
||||||
QualType T = CE->getType();
|
QualType T = CE->getType();
|
||||||
|
|
||||||
if (!T->isIntegerType())
|
if (!T->isIntegralOrEnumerationType())
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
|
||||||
uint64_t newBits = Ctx.getTypeSize(T);
|
uint64_t newBits = Ctx.getTypeSize(T);
|
||||||
@@ -1250,7 +1251,8 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr,
|
|||||||
// We reached a non-cast. Is it a symbolic value?
|
// We reached a non-cast. Is it a symbolic value?
|
||||||
QualType T = Ex->getType();
|
QualType T = Ex->getType();
|
||||||
|
|
||||||
if (!bitsInit || !T->isIntegerType() || Ctx.getTypeSize(T) > bits)
|
if (!bitsInit || !T->isIntegralOrEnumerationType() ||
|
||||||
|
Ctx.getTypeSize(T) > bits)
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
|
||||||
return state->getSVal(Ex, LCtx);
|
return state->getSVal(Ex, LCtx);
|
||||||
@@ -1342,7 +1344,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
|
|||||||
if (X.isUnknownOrUndef()) {
|
if (X.isUnknownOrUndef()) {
|
||||||
// Give it a chance to recover from unknown.
|
// Give it a chance to recover from unknown.
|
||||||
if (const Expr *Ex = dyn_cast<Expr>(Condition)) {
|
if (const Expr *Ex = dyn_cast<Expr>(Condition)) {
|
||||||
if (Ex->getType()->isIntegerType()) {
|
if (Ex->getType()->isIntegralOrEnumerationType()) {
|
||||||
// Try to recover some path-sensitivity. Right now casts of symbolic
|
// Try to recover some path-sensitivity. Right now casts of symbolic
|
||||||
// integers that promote their values are currently not tracked well.
|
// integers that promote their values are currently not tracked well.
|
||||||
// If 'Condition' is such an expression, try and recover the
|
// If 'Condition' is such an expression, try and recover the
|
||||||
|
|||||||
@@ -68,12 +68,14 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
|
|||||||
// SymSymExpr.
|
// SymSymExpr.
|
||||||
unsigned Count = currBldrCtx->blockCount();
|
unsigned Count = currBldrCtx->blockCount();
|
||||||
if (LeftV.getAs<Loc>() &&
|
if (LeftV.getAs<Loc>() &&
|
||||||
RHS->getType()->isIntegerType() && RightV.isUnknown()) {
|
RHS->getType()->isIntegralOrEnumerationType() &&
|
||||||
|
RightV.isUnknown()) {
|
||||||
RightV = svalBuilder.conjureSymbolVal(RHS, LCtx, RHS->getType(),
|
RightV = svalBuilder.conjureSymbolVal(RHS, LCtx, RHS->getType(),
|
||||||
Count);
|
Count);
|
||||||
}
|
}
|
||||||
if (RightV.getAs<Loc>() &&
|
if (RightV.getAs<Loc>() &&
|
||||||
LHS->getType()->isIntegerType() && LeftV.isUnknown()) {
|
LHS->getType()->isIntegralOrEnumerationType() &&
|
||||||
|
LeftV.isUnknown()) {
|
||||||
LeftV = svalBuilder.conjureSymbolVal(LHS, LCtx, LHS->getType(),
|
LeftV = svalBuilder.conjureSymbolVal(LHS, LCtx, LHS->getType(),
|
||||||
Count);
|
Count);
|
||||||
}
|
}
|
||||||
@@ -662,8 +664,9 @@ VisitOffsetOfExpr(const OffsetOfExpr *OOE,
|
|||||||
APSInt IV;
|
APSInt IV;
|
||||||
if (OOE->EvaluateAsInt(IV, getContext())) {
|
if (OOE->EvaluateAsInt(IV, getContext())) {
|
||||||
assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType()));
|
assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType()));
|
||||||
assert(OOE->getType()->isIntegerType());
|
assert(OOE->getType()->isBuiltinType());
|
||||||
assert(IV.isSigned() == OOE->getType()->isSignedIntegerOrEnumerationType());
|
assert(OOE->getType()->getAs<BuiltinType>()->isInteger());
|
||||||
|
assert(IV.isSigned() == OOE->getType()->isSignedIntegerType());
|
||||||
SVal X = svalBuilder.makeIntVal(IV);
|
SVal X = svalBuilder.makeIntVal(IV);
|
||||||
B.generateNode(OOE, Pred,
|
B.generateNode(OOE, Pred,
|
||||||
Pred->getState()->BindExpr(OOE, Pred->getLocationContext(),
|
Pred->getState()->BindExpr(OOE, Pred->getLocationContext(),
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const {
|
|||||||
|
|
||||||
if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
|
if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
|
||||||
QualType T = TR->getValueType();
|
QualType T = TR->getValueType();
|
||||||
if (Loc::isLocType(T) || T->isIntegerType())
|
if (Loc::isLocType(T) || T->isIntegralOrEnumerationType())
|
||||||
return getSVal(R);
|
return getSVal(R);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1907,7 +1907,7 @@ RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B,
|
|||||||
|
|
||||||
if (Loc::isLocType(T))
|
if (Loc::isLocType(T))
|
||||||
V = svalBuilder.makeNull();
|
V = svalBuilder.makeNull();
|
||||||
else if (T->isIntegerType())
|
else if (T->isIntegralOrEnumerationType())
|
||||||
V = svalBuilder.makeZeroVal(T);
|
V = svalBuilder.makeZeroVal(T);
|
||||||
else if (T->isStructureOrClassType() || T->isArrayType()) {
|
else if (T->isStructureOrClassType() || T->isArrayType()) {
|
||||||
// Set the default value to a zero constant when it is a structure
|
// Set the default value to a zero constant when it is a structure
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) {
|
|||||||
if (Loc::isLocType(type))
|
if (Loc::isLocType(type))
|
||||||
return makeNull();
|
return makeNull();
|
||||||
|
|
||||||
if (type->isIntegerType())
|
if (type->isIntegralOrEnumerationType())
|
||||||
return makeIntVal(0, type);
|
return makeIntVal(0, type);
|
||||||
|
|
||||||
// FIXME: Handle floats.
|
// FIXME: Handle floats.
|
||||||
@@ -327,11 +327,11 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
|
|||||||
return val;
|
return val;
|
||||||
|
|
||||||
// Check for casts from pointers to integers.
|
// Check for casts from pointers to integers.
|
||||||
if (castTy->isIntegerType() && Loc::isLocType(originalTy))
|
if (castTy->isIntegralOrEnumerationType() && Loc::isLocType(originalTy))
|
||||||
return evalCastFromLoc(val.castAs<Loc>(), castTy);
|
return evalCastFromLoc(val.castAs<Loc>(), castTy);
|
||||||
|
|
||||||
// Check for casts from integers to pointers.
|
// Check for casts from integers to pointers.
|
||||||
if (Loc::isLocType(castTy) && originalTy->isIntegerType()) {
|
if (Loc::isLocType(castTy) && originalTy->isIntegralOrEnumerationType()) {
|
||||||
if (Optional<nonloc::LocAsInteger> LV = val.getAs<nonloc::LocAsInteger>()) {
|
if (Optional<nonloc::LocAsInteger> LV = val.getAs<nonloc::LocAsInteger>()) {
|
||||||
if (const MemRegion *R = LV->getLoc().getAsRegion()) {
|
if (const MemRegion *R = LV->getLoc().getAsRegion()) {
|
||||||
StoreManager &storeMgr = StateMgr.getStoreManager();
|
StoreManager &storeMgr = StateMgr.getStoreManager();
|
||||||
@@ -361,7 +361,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
|
|||||||
|
|
||||||
// Are we casting from an array to an integer? If so, cast the decayed
|
// Are we casting from an array to an integer? If so, cast the decayed
|
||||||
// pointer value to an integer.
|
// pointer value to an integer.
|
||||||
assert(castTy->isIntegerType());
|
assert(castTy->isIntegralOrEnumerationType());
|
||||||
|
|
||||||
// FIXME: Keep these here for now in case we decide soon that we
|
// FIXME: Keep these here for now in case we decide soon that we
|
||||||
// need the original decayed type.
|
// need the original decayed type.
|
||||||
@@ -373,7 +373,7 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
|
|||||||
// Check for casts from a region to a specific type.
|
// Check for casts from a region to a specific type.
|
||||||
if (const MemRegion *R = val.getAsRegion()) {
|
if (const MemRegion *R = val.getAsRegion()) {
|
||||||
// Handle other casts of locations to integers.
|
// Handle other casts of locations to integers.
|
||||||
if (castTy->isIntegerType())
|
if (castTy->isIntegralOrEnumerationType())
|
||||||
return evalCastFromLoc(loc::MemRegionVal(R), castTy);
|
return evalCastFromLoc(loc::MemRegionVal(R), castTy);
|
||||||
|
|
||||||
// FIXME: We should handle the case where we strip off view layers to get
|
// FIXME: We should handle the case where we strip off view layers to get
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State,
|
|||||||
QualType T = Sym->getType();
|
QualType T = Sym->getType();
|
||||||
|
|
||||||
// None of the constraint solvers currently support non-integer types.
|
// None of the constraint solvers currently support non-integer types.
|
||||||
if (!T->isIntegerType())
|
if (!T->isIntegralOrEnumerationType())
|
||||||
return State;
|
return State;
|
||||||
|
|
||||||
const llvm::APSInt &zero = BVF.getValue(0, T);
|
const llvm::APSInt &zero = BVF.getValue(0, T);
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) {
|
|||||||
|
|
||||||
// Only handle casts from integers to integers - if val is an integer constant
|
// Only handle casts from integers to integers - if val is an integer constant
|
||||||
// being cast to a non integer type, produce unknown.
|
// being cast to a non integer type, produce unknown.
|
||||||
if (!isLocType && !castTy->isIntegerType())
|
if (!isLocType && !castTy->isIntegralOrEnumerationType())
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
|
||||||
llvm::APSInt i = val.castAs<nonloc::ConcreteInt>().getValue();
|
llvm::APSInt i = val.castAs<nonloc::ConcreteInt>().getValue();
|
||||||
@@ -137,7 +137,7 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) {
|
|||||||
if (castTy->isUnionType())
|
if (castTy->isUnionType())
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
|
||||||
if (castTy->isIntegerType()) {
|
if (castTy->isIntegralOrEnumerationType()) {
|
||||||
unsigned BitWidth = Context.getTypeSize(castTy);
|
unsigned BitWidth = Context.getTypeSize(castTy);
|
||||||
|
|
||||||
if (!val.getAs<loc::ConcreteInt>())
|
if (!val.getAs<loc::ConcreteInt>())
|
||||||
|
|||||||
@@ -340,8 +340,8 @@ bool SymbolManager::canSymbolicate(QualType T) {
|
|||||||
if (Loc::isLocType(T))
|
if (Loc::isLocType(T))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (T->isIntegerType())
|
if (T->isIntegralOrEnumerationType())
|
||||||
return T->isScalarType();
|
return true;
|
||||||
|
|
||||||
if (T->isRecordType() && !T->isUnionType())
|
if (T->isRecordType() && !T->isUnionType())
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
26
clang/test/Analysis/enum.cpp
Normal file
26
clang/test/Analysis/enum.cpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// RUN: %clang_cc1 -analyze -std=c++11 -analyzer-checker=debug.ExprInspection %s
|
||||||
|
|
||||||
|
void clang_analyzer_eval(bool);
|
||||||
|
|
||||||
|
enum class Foo {
|
||||||
|
Zero
|
||||||
|
};
|
||||||
|
|
||||||
|
bool pr15703(int x) {
|
||||||
|
return Foo::Zero == (Foo)x; // don't crash
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCasting(int i) {
|
||||||
|
Foo f = static_cast<Foo>(i);
|
||||||
|
int j = static_cast<int>(f);
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
clang_analyzer_eval(f == Foo::Zero); // expected-warning{{TRUE}}
|
||||||
|
clang_analyzer_eval(j == 0); // expected-warning{{TRUE}}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clang_analyzer_eval(f == Foo::Zero); // expected-warning{{FALSE}}
|
||||||
|
clang_analyzer_eval(j == 0); // expected-warning{{FALSE}}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user