forked from OSchip/llvm-project
[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 llvm-svn: 340984
This commit is contained in:
parent
1e4498869d
commit
4e864b8329
|
@ -122,7 +122,7 @@ public:
|
|||
/// Each region is a subregion of itself.
|
||||
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,
|
||||
/// 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>
|
||||
const RegionTy* MemRegion::getAs() const {
|
||||
if (const auto *RT = dyn_cast<RegionTy>(this))
|
||||
|
@ -1326,6 +1367,14 @@ public:
|
|||
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 BlockCodeRegion *getBlockCodeRegion(const BlockDecl *BD,
|
||||
CanQualType locTy,
|
||||
|
|
|
@ -68,6 +68,7 @@ ABSTRACT_REGION(SubRegion, MemRegion)
|
|||
ABSTRACT_REGION(TypedValueRegion, TypedRegion)
|
||||
REGION(CompoundLiteralRegion, TypedValueRegion)
|
||||
REGION(CXXBaseObjectRegion, TypedValueRegion)
|
||||
REGION(CXXDerivedObjectRegion, TypedValueRegion)
|
||||
REGION(CXXTempObjectRegion, TypedValueRegion)
|
||||
REGION(CXXThisRegion, TypedValueRegion)
|
||||
ABSTRACT_REGION(DeclRegion, TypedValueRegion)
|
||||
|
|
|
@ -225,6 +225,10 @@ QualType CXXBaseObjectRegion::getValueType() const {
|
|||
return QualType(getDecl()->getTypeForDecl(), 0);
|
||||
}
|
||||
|
||||
QualType CXXDerivedObjectRegion::getValueType() const {
|
||||
return QualType(getDecl()->getTypeForDecl(), 0);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// FoldingSet profiling.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -404,6 +408,17 @@ void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
|
|||
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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -475,7 +490,11 @@ void CXXTempObjectRegion::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 {
|
||||
|
@ -483,7 +502,7 @@ void CXXThisRegion::dumpToStream(raw_ostream &os) const {
|
|||
}
|
||||
|
||||
void ElementRegion::dumpToStream(raw_ostream &os) const {
|
||||
os << "element{" << superRegion << ','
|
||||
os << "Element{" << superRegion << ','
|
||||
<< Index << ',' << getElementType().getAsString() << '}';
|
||||
}
|
||||
|
||||
|
@ -492,7 +511,7 @@ void FieldRegion::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 {
|
||||
|
@ -630,6 +649,14 @@ void CXXBaseObjectRegion::printPrettyAsExpr(raw_ostream &os) const {
|
|||
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 VariableName;
|
||||
std::string ArrayIndices;
|
||||
|
@ -1061,6 +1088,12 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD,
|
|||
return getSubRegion<CXXBaseObjectRegion>(RD, IsVirtual, Super);
|
||||
}
|
||||
|
||||
const CXXDerivedObjectRegion *
|
||||
MemRegionManager::getCXXDerivedObjectRegion(const CXXRecordDecl *RD,
|
||||
const SubRegion *Super) {
|
||||
return getSubRegion<CXXDerivedObjectRegion>(RD, Super);
|
||||
}
|
||||
|
||||
const CXXThisRegion*
|
||||
MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
|
||||
const LocationContext *LC) {
|
||||
|
@ -1131,6 +1164,7 @@ const MemRegion *MemRegion::getBaseRegion() const {
|
|||
case MemRegion::FieldRegionKind:
|
||||
case MemRegion::ObjCIvarRegionKind:
|
||||
case MemRegion::CXXBaseObjectRegionKind:
|
||||
case MemRegion::CXXDerivedObjectRegionKind:
|
||||
R = cast<SubRegion>(R)->getSuperRegion();
|
||||
continue;
|
||||
default:
|
||||
|
@ -1149,7 +1183,7 @@ bool MemRegion::isSubRegionOf(const MemRegion *R) const {
|
|||
// View handling.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const {
|
||||
const MemRegion *MemRegion::StripCasts(bool StripBaseAndDerivedCasts) const {
|
||||
const MemRegion *R = this;
|
||||
while (true) {
|
||||
switch (R->getKind()) {
|
||||
|
@ -1161,9 +1195,10 @@ const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const {
|
|||
break;
|
||||
}
|
||||
case CXXBaseObjectRegionKind:
|
||||
if (!StripBaseCasts)
|
||||
case CXXDerivedObjectRegionKind:
|
||||
if (!StripBaseAndDerivedCasts)
|
||||
return R;
|
||||
R = cast<CXXBaseObjectRegion>(R)->getSuperRegion();
|
||||
R = cast<TypedValueRegion>(R)->getSuperRegion();
|
||||
break;
|
||||
default:
|
||||
return R;
|
||||
|
@ -1344,6 +1379,12 @@ static RegionOffset calculateOffset(const MemRegion *R) {
|
|||
Offset += BaseOffset.getQuantity() * R->getContext().getCharWidth();
|
||||
break;
|
||||
}
|
||||
|
||||
case MemRegion::CXXDerivedObjectRegionKind: {
|
||||
// TODO: Store the base type in the CXXDerivedObjectRegion and use it.
|
||||
goto Finish;
|
||||
}
|
||||
|
||||
case MemRegion::ElementRegionKind: {
|
||||
const auto *ER = cast<ElementRegion>(R);
|
||||
R = ER->getSuperRegion();
|
||||
|
|
|
@ -62,7 +62,9 @@ private:
|
|||
: P(r, k), Data(offset) {
|
||||
assert(r && "Must have known regions.");
|
||||
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:
|
||||
|
||||
|
|
|
@ -138,6 +138,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
|
|||
case MemRegion::VarRegionKind:
|
||||
case MemRegion::CXXTempObjectRegionKind:
|
||||
case MemRegion::CXXBaseObjectRegionKind:
|
||||
case MemRegion::CXXDerivedObjectRegionKind:
|
||||
return MakeElementRegion(cast<SubRegion>(R), PointeeTy);
|
||||
|
||||
case MemRegion::ElementRegionKind: {
|
||||
|
@ -272,9 +273,8 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, const CXXBasePath &Path) {
|
|||
|
||||
SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType,
|
||||
bool IsVirtual) {
|
||||
Optional<loc::MemRegionVal> DerivedRegVal =
|
||||
Derived.getAs<loc::MemRegionVal>();
|
||||
if (!DerivedRegVal)
|
||||
const MemRegion *DerivedReg = Derived.getAsRegion();
|
||||
if (!DerivedReg)
|
||||
return Derived;
|
||||
|
||||
const CXXRecordDecl *BaseDecl = BaseType->getPointeeCXXRecordDecl();
|
||||
|
@ -282,8 +282,18 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType,
|
|||
BaseDecl = BaseType->getAsCXXRecordDecl();
|
||||
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(
|
||||
BaseDecl, cast<SubRegion>(DerivedRegVal->getRegion()), IsVirtual);
|
||||
BaseDecl, cast<SubRegion>(DerivedReg), IsVirtual);
|
||||
|
||||
return loc::MemRegionVal(BaseReg);
|
||||
}
|
||||
|
@ -365,6 +375,10 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType,
|
|||
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.
|
||||
Failed = isa<TypedValueRegion>(MR);
|
||||
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) {
|
||||
double y = (double)x;
|
||||
|
@ -41,3 +43,32 @@ bool retrievePointerFromBoolean(int *p) {
|
|||
*reinterpret_cast<int **>(&q) = p;
|
||||
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