mirror of https://github.com/microsoft/clang.git
[analyzer] Support modeling no-op BaseToDerived casts in ExprEngine.
Introduce a new MemRegion sub-class, CXXDerivedObjectRegion, which is the opposite of CXXBaseObjectRegion, to represent such casts. Such region is a bit weird because it is by design bigger than its super-region. But it's not harmful when it is put on top of a SymbolicRegion that has unknown extent anyway. Offset computation for CXXDerivedObjectRegion and proper modeling of casts still remains to be implemented. Differential Revision: https://reviews.llvm.org/D51191 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@340984 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
19ada6cb9b
commit
0d516dba5e
|
@ -122,7 +122,7 @@ public:
|
||||||
/// Each region is a subregion of itself.
|
/// Each region is a subregion of itself.
|
||||||
virtual bool isSubRegionOf(const MemRegion *R) const;
|
virtual bool isSubRegionOf(const MemRegion *R) const;
|
||||||
|
|
||||||
const MemRegion *StripCasts(bool StripBaseCasts = true) const;
|
const MemRegion *StripCasts(bool StripBaseAndDerivedCasts = true) const;
|
||||||
|
|
||||||
/// If this is a symbolic region, returns the region. Otherwise,
|
/// If this is a symbolic region, returns the region. Otherwise,
|
||||||
/// goes up the base chain looking for the first symbolic base region.
|
/// goes up the base chain looking for the first symbolic base region.
|
||||||
|
@ -1176,6 +1176,47 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// CXXDerivedObjectRegion represents a derived-class object that surrounds
|
||||||
|
// a C++ object. It is identified by the derived class declaration and the
|
||||||
|
// region of its parent object. It is a bit counter-intuitive (but not otherwise
|
||||||
|
// unseen) that this region represents a larger segment of memory that its
|
||||||
|
// super-region.
|
||||||
|
class CXXDerivedObjectRegion : public TypedValueRegion {
|
||||||
|
friend class MemRegionManager;
|
||||||
|
|
||||||
|
const CXXRecordDecl *DerivedD;
|
||||||
|
|
||||||
|
CXXDerivedObjectRegion(const CXXRecordDecl *DerivedD, const SubRegion *SReg)
|
||||||
|
: TypedValueRegion(SReg, CXXDerivedObjectRegionKind), DerivedD(DerivedD) {
|
||||||
|
assert(DerivedD);
|
||||||
|
// In case of a concrete region, it should always be possible to model
|
||||||
|
// the base-to-derived cast by undoing a previous derived-to-base cast,
|
||||||
|
// otherwise the cast is most likely ill-formed.
|
||||||
|
assert(SReg->getSymbolicBase() &&
|
||||||
|
"Should have unwrapped a base region instead!");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ProfileRegion(llvm::FoldingSetNodeID &ID, const CXXRecordDecl *RD,
|
||||||
|
const MemRegion *SReg);
|
||||||
|
|
||||||
|
public:
|
||||||
|
const CXXRecordDecl *getDecl() const { return DerivedD; }
|
||||||
|
|
||||||
|
QualType getValueType() const override;
|
||||||
|
|
||||||
|
void dumpToStream(raw_ostream &os) const override;
|
||||||
|
|
||||||
|
void Profile(llvm::FoldingSetNodeID &ID) const override;
|
||||||
|
|
||||||
|
bool canPrintPrettyAsExpr() const override;
|
||||||
|
|
||||||
|
void printPrettyAsExpr(raw_ostream &os) const override;
|
||||||
|
|
||||||
|
static bool classof(const MemRegion *region) {
|
||||||
|
return region->getKind() == CXXDerivedObjectRegionKind;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template<typename RegionTy>
|
template<typename RegionTy>
|
||||||
const RegionTy* MemRegion::getAs() const {
|
const RegionTy* MemRegion::getAs() const {
|
||||||
if (const auto *RT = dyn_cast<RegionTy>(this))
|
if (const auto *RT = dyn_cast<RegionTy>(this))
|
||||||
|
@ -1326,6 +1367,14 @@ public:
|
||||||
baseReg->isVirtual());
|
baseReg->isVirtual());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a CXXDerivedObjectRegion with the given derived class for region
|
||||||
|
/// \p Super. This should not be used for casting an existing
|
||||||
|
/// CXXBaseObjectRegion back to the derived type; instead, CXXBaseObjectRegion
|
||||||
|
/// should be removed.
|
||||||
|
const CXXDerivedObjectRegion *
|
||||||
|
getCXXDerivedObjectRegion(const CXXRecordDecl *BaseClass,
|
||||||
|
const SubRegion *Super);
|
||||||
|
|
||||||
const FunctionCodeRegion *getFunctionCodeRegion(const NamedDecl *FD);
|
const FunctionCodeRegion *getFunctionCodeRegion(const NamedDecl *FD);
|
||||||
const BlockCodeRegion *getBlockCodeRegion(const BlockDecl *BD,
|
const BlockCodeRegion *getBlockCodeRegion(const BlockDecl *BD,
|
||||||
CanQualType locTy,
|
CanQualType locTy,
|
||||||
|
|
|
@ -68,6 +68,7 @@ ABSTRACT_REGION(SubRegion, MemRegion)
|
||||||
ABSTRACT_REGION(TypedValueRegion, TypedRegion)
|
ABSTRACT_REGION(TypedValueRegion, TypedRegion)
|
||||||
REGION(CompoundLiteralRegion, TypedValueRegion)
|
REGION(CompoundLiteralRegion, TypedValueRegion)
|
||||||
REGION(CXXBaseObjectRegion, TypedValueRegion)
|
REGION(CXXBaseObjectRegion, TypedValueRegion)
|
||||||
|
REGION(CXXDerivedObjectRegion, TypedValueRegion)
|
||||||
REGION(CXXTempObjectRegion, TypedValueRegion)
|
REGION(CXXTempObjectRegion, TypedValueRegion)
|
||||||
REGION(CXXThisRegion, TypedValueRegion)
|
REGION(CXXThisRegion, TypedValueRegion)
|
||||||
ABSTRACT_REGION(DeclRegion, TypedValueRegion)
|
ABSTRACT_REGION(DeclRegion, TypedValueRegion)
|
||||||
|
|
|
@ -225,6 +225,10 @@ QualType CXXBaseObjectRegion::getValueType() const {
|
||||||
return QualType(getDecl()->getTypeForDecl(), 0);
|
return QualType(getDecl()->getTypeForDecl(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QualType CXXDerivedObjectRegion::getValueType() const {
|
||||||
|
return QualType(getDecl()->getTypeForDecl(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// FoldingSet profiling.
|
// FoldingSet profiling.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -404,6 +408,17 @@ void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
|
||||||
ProfileRegion(ID, getDecl(), isVirtual(), superRegion);
|
ProfileRegion(ID, getDecl(), isVirtual(), superRegion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CXXDerivedObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
|
||||||
|
const CXXRecordDecl *RD,
|
||||||
|
const MemRegion *SReg) {
|
||||||
|
ID.AddPointer(RD);
|
||||||
|
ID.AddPointer(SReg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CXXDerivedObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
|
||||||
|
ProfileRegion(ID, getDecl(), superRegion);
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Region anchors.
|
// Region anchors.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -475,7 +490,11 @@ void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const {
|
void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const {
|
||||||
os << "base{" << superRegion << ',' << getDecl()->getName() << '}';
|
os << "Base{" << superRegion << ',' << getDecl()->getName() << '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
void CXXDerivedObjectRegion::dumpToStream(raw_ostream &os) const {
|
||||||
|
os << "Derived{" << superRegion << ',' << getDecl()->getName() << '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXXThisRegion::dumpToStream(raw_ostream &os) const {
|
void CXXThisRegion::dumpToStream(raw_ostream &os) const {
|
||||||
|
@ -483,7 +502,7 @@ void CXXThisRegion::dumpToStream(raw_ostream &os) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElementRegion::dumpToStream(raw_ostream &os) const {
|
void ElementRegion::dumpToStream(raw_ostream &os) const {
|
||||||
os << "element{" << superRegion << ','
|
os << "Element{" << superRegion << ','
|
||||||
<< Index << ',' << getElementType().getAsString() << '}';
|
<< Index << ',' << getElementType().getAsString() << '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,7 +511,7 @@ void FieldRegion::dumpToStream(raw_ostream &os) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjCIvarRegion::dumpToStream(raw_ostream &os) const {
|
void ObjCIvarRegion::dumpToStream(raw_ostream &os) const {
|
||||||
os << "ivar{" << superRegion << ',' << *getDecl() << '}';
|
os << "Ivar{" << superRegion << ',' << *getDecl() << '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringRegion::dumpToStream(raw_ostream &os) const {
|
void StringRegion::dumpToStream(raw_ostream &os) const {
|
||||||
|
@ -630,6 +649,14 @@ void CXXBaseObjectRegion::printPrettyAsExpr(raw_ostream &os) const {
|
||||||
superRegion->printPrettyAsExpr(os);
|
superRegion->printPrettyAsExpr(os);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CXXDerivedObjectRegion::canPrintPrettyAsExpr() const {
|
||||||
|
return superRegion->canPrintPrettyAsExpr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CXXDerivedObjectRegion::printPrettyAsExpr(raw_ostream &os) const {
|
||||||
|
superRegion->printPrettyAsExpr(os);
|
||||||
|
}
|
||||||
|
|
||||||
std::string MemRegion::getDescriptiveName(bool UseQuotes) const {
|
std::string MemRegion::getDescriptiveName(bool UseQuotes) const {
|
||||||
std::string VariableName;
|
std::string VariableName;
|
||||||
std::string ArrayIndices;
|
std::string ArrayIndices;
|
||||||
|
@ -1061,6 +1088,12 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD,
|
||||||
return getSubRegion<CXXBaseObjectRegion>(RD, IsVirtual, Super);
|
return getSubRegion<CXXBaseObjectRegion>(RD, IsVirtual, Super);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CXXDerivedObjectRegion *
|
||||||
|
MemRegionManager::getCXXDerivedObjectRegion(const CXXRecordDecl *RD,
|
||||||
|
const SubRegion *Super) {
|
||||||
|
return getSubRegion<CXXDerivedObjectRegion>(RD, Super);
|
||||||
|
}
|
||||||
|
|
||||||
const CXXThisRegion*
|
const CXXThisRegion*
|
||||||
MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
|
MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
|
||||||
const LocationContext *LC) {
|
const LocationContext *LC) {
|
||||||
|
@ -1131,6 +1164,7 @@ const MemRegion *MemRegion::getBaseRegion() const {
|
||||||
case MemRegion::FieldRegionKind:
|
case MemRegion::FieldRegionKind:
|
||||||
case MemRegion::ObjCIvarRegionKind:
|
case MemRegion::ObjCIvarRegionKind:
|
||||||
case MemRegion::CXXBaseObjectRegionKind:
|
case MemRegion::CXXBaseObjectRegionKind:
|
||||||
|
case MemRegion::CXXDerivedObjectRegionKind:
|
||||||
R = cast<SubRegion>(R)->getSuperRegion();
|
R = cast<SubRegion>(R)->getSuperRegion();
|
||||||
continue;
|
continue;
|
||||||
default:
|
default:
|
||||||
|
@ -1149,7 +1183,7 @@ bool MemRegion::isSubRegionOf(const MemRegion *R) const {
|
||||||
// View handling.
|
// View handling.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const {
|
const MemRegion *MemRegion::StripCasts(bool StripBaseAndDerivedCasts) const {
|
||||||
const MemRegion *R = this;
|
const MemRegion *R = this;
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (R->getKind()) {
|
switch (R->getKind()) {
|
||||||
|
@ -1161,9 +1195,10 @@ const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CXXBaseObjectRegionKind:
|
case CXXBaseObjectRegionKind:
|
||||||
if (!StripBaseCasts)
|
case CXXDerivedObjectRegionKind:
|
||||||
|
if (!StripBaseAndDerivedCasts)
|
||||||
return R;
|
return R;
|
||||||
R = cast<CXXBaseObjectRegion>(R)->getSuperRegion();
|
R = cast<TypedValueRegion>(R)->getSuperRegion();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return R;
|
return R;
|
||||||
|
@ -1344,6 +1379,12 @@ static RegionOffset calculateOffset(const MemRegion *R) {
|
||||||
Offset += BaseOffset.getQuantity() * R->getContext().getCharWidth();
|
Offset += BaseOffset.getQuantity() * R->getContext().getCharWidth();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MemRegion::CXXDerivedObjectRegionKind: {
|
||||||
|
// TODO: Store the base type in the CXXDerivedObjectRegion and use it.
|
||||||
|
goto Finish;
|
||||||
|
}
|
||||||
|
|
||||||
case MemRegion::ElementRegionKind: {
|
case MemRegion::ElementRegionKind: {
|
||||||
const auto *ER = cast<ElementRegion>(R);
|
const auto *ER = cast<ElementRegion>(R);
|
||||||
R = ER->getSuperRegion();
|
R = ER->getSuperRegion();
|
||||||
|
|
|
@ -62,7 +62,9 @@ private:
|
||||||
: P(r, k), Data(offset) {
|
: P(r, k), Data(offset) {
|
||||||
assert(r && "Must have known regions.");
|
assert(r && "Must have known regions.");
|
||||||
assert(getOffset() == offset && "Failed to store offset");
|
assert(getOffset() == offset && "Failed to store offset");
|
||||||
assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r)) && "Not a base");
|
assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r) ||
|
||||||
|
isa <CXXDerivedObjectRegion>(r)) &&
|
||||||
|
"Not a base");
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
|
||||||
case MemRegion::VarRegionKind:
|
case MemRegion::VarRegionKind:
|
||||||
case MemRegion::CXXTempObjectRegionKind:
|
case MemRegion::CXXTempObjectRegionKind:
|
||||||
case MemRegion::CXXBaseObjectRegionKind:
|
case MemRegion::CXXBaseObjectRegionKind:
|
||||||
|
case MemRegion::CXXDerivedObjectRegionKind:
|
||||||
return MakeElementRegion(cast<SubRegion>(R), PointeeTy);
|
return MakeElementRegion(cast<SubRegion>(R), PointeeTy);
|
||||||
|
|
||||||
case MemRegion::ElementRegionKind: {
|
case MemRegion::ElementRegionKind: {
|
||||||
|
@ -272,9 +273,8 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, const CXXBasePath &Path) {
|
||||||
|
|
||||||
SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType,
|
SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType,
|
||||||
bool IsVirtual) {
|
bool IsVirtual) {
|
||||||
Optional<loc::MemRegionVal> DerivedRegVal =
|
const MemRegion *DerivedReg = Derived.getAsRegion();
|
||||||
Derived.getAs<loc::MemRegionVal>();
|
if (!DerivedReg)
|
||||||
if (!DerivedRegVal)
|
|
||||||
return Derived;
|
return Derived;
|
||||||
|
|
||||||
const CXXRecordDecl *BaseDecl = BaseType->getPointeeCXXRecordDecl();
|
const CXXRecordDecl *BaseDecl = BaseType->getPointeeCXXRecordDecl();
|
||||||
|
@ -282,8 +282,18 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType,
|
||||||
BaseDecl = BaseType->getAsCXXRecordDecl();
|
BaseDecl = BaseType->getAsCXXRecordDecl();
|
||||||
assert(BaseDecl && "not a C++ object?");
|
assert(BaseDecl && "not a C++ object?");
|
||||||
|
|
||||||
|
if (const auto *AlreadyDerivedReg =
|
||||||
|
dyn_cast<CXXDerivedObjectRegion>(DerivedReg)) {
|
||||||
|
if (const auto *SR =
|
||||||
|
dyn_cast<SymbolicRegion>(AlreadyDerivedReg->getSuperRegion()))
|
||||||
|
if (SR->getSymbol()->getType()->getPointeeCXXRecordDecl() == BaseDecl)
|
||||||
|
return loc::MemRegionVal(SR);
|
||||||
|
|
||||||
|
DerivedReg = AlreadyDerivedReg->getSuperRegion();
|
||||||
|
}
|
||||||
|
|
||||||
const MemRegion *BaseReg = MRMgr.getCXXBaseObjectRegion(
|
const MemRegion *BaseReg = MRMgr.getCXXBaseObjectRegion(
|
||||||
BaseDecl, cast<SubRegion>(DerivedRegVal->getRegion()), IsVirtual);
|
BaseDecl, cast<SubRegion>(DerivedReg), IsVirtual);
|
||||||
|
|
||||||
return loc::MemRegionVal(BaseReg);
|
return loc::MemRegionVal(BaseReg);
|
||||||
}
|
}
|
||||||
|
@ -365,6 +375,10 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType,
|
||||||
MR = Uncasted;
|
MR = Uncasted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) {
|
||||||
|
return loc::MemRegionVal(MRMgr.getCXXDerivedObjectRegion(TargetClass, SR));
|
||||||
|
}
|
||||||
|
|
||||||
// We failed if the region we ended up with has perfect type info.
|
// We failed if the region we ended up with has perfect type info.
|
||||||
Failed = isa<TypedValueRegion>(MR);
|
Failed = isa<TypedValueRegion>(MR);
|
||||||
return UnknownVal();
|
return UnknownVal();
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-store=region -verify %s
|
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-store=region -verify %s
|
||||||
|
|
||||||
|
void clang_analyzer_eval(bool);
|
||||||
|
|
||||||
bool PR14634(int x) {
|
bool PR14634(int x) {
|
||||||
double y = (double)x;
|
double y = (double)x;
|
||||||
|
@ -41,3 +43,32 @@ bool retrievePointerFromBoolean(int *p) {
|
||||||
*reinterpret_cast<int **>(&q) = p;
|
*reinterpret_cast<int **>(&q) = p;
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace base_to_derived {
|
||||||
|
struct A {};
|
||||||
|
struct B : public A{};
|
||||||
|
|
||||||
|
void foo(A* a) {
|
||||||
|
B* b = (B* ) a;
|
||||||
|
A* a2 = (A *) b;
|
||||||
|
clang_analyzer_eval(a2 == a); // expected-warning{{TRUE}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace base_to_derived_double_inheritance {
|
||||||
|
struct A {
|
||||||
|
int x;
|
||||||
|
};
|
||||||
|
struct B {
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
struct C : A, B {};
|
||||||
|
|
||||||
|
void foo(B *b) {
|
||||||
|
C *c = (C *)b;
|
||||||
|
b->y = 1;
|
||||||
|
clang_analyzer_eval(c->x); // expected-warning{{UNKNOWN}}
|
||||||
|
clang_analyzer_eval(c->y); // expected-warning{{TRUE}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue