diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -262,6 +262,10 @@ accumCXXBase(llvm::iterator_range PathRange, const nonloc::PointerToMember &PTM, const clang::CastKind &kind); + const PointerToMemberData * + basesForReinterpretCast(const CXXReinterpretCastExpr *CastE, + const nonloc::PointerToMember &PTM); + const llvm::APSInt* evalAPSInt(BinaryOperator::Opcode Op, const llvm::APSInt& V1, const llvm::APSInt& V2); diff --git a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp --- a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" +#include "clang/AST/CXXInheritance.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" @@ -22,6 +23,7 @@ #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" #include #include #include @@ -239,13 +241,90 @@ return getPointerToMemberData(ND, ReducedBaseSpecList); } - // FIXME: Reinterpret casts on member-pointers are not handled properly by - // this code for (const CXXBaseSpecifier *I : llvm::reverse(PathRange)) BaseSpecList = prependCXXBase(I, BaseSpecList); return getPointerToMemberData(ND, BaseSpecList); } +const CXXRecordDecl *retrieveCXXRecord(const NamedDecl *ND) { + if (const auto *FD = llvm::dyn_cast(ND)) { + const RecordDecl *RD = FD->getParent(); + return llvm::dyn_cast(RD); + } + if (const auto *MD = llvm::dyn_cast(ND)) { + return MD->getParent(); + } + if (const auto *IFD = llvm::dyn_cast(ND)) { + if (const FieldDecl *FD = IFD->getAnonField()) { + const RecordDecl *RD = FD->getParent(); + return llvm::dyn_cast(RD); + } + } + return nullptr; +} + +std::pair, bool> +findPathToBase(const CXXRecordDecl *Start, const CXXRecordDecl *End) { + CXXBasePaths BasePaths; + llvm::SmallVector Path; + bool Found = Start->isDerivedFrom(End, BasePaths); + if (!Found) + return {Path, false}; + CXXBasePath &ActualBasePath = BasePaths.front(); + for (const CXXBasePathElement &Elem : ActualBasePath) { + Path.push_back(Elem.Base); + } + return {Path, true}; +} + +const PointerToMemberData * +BasicValueFactory::basesForReinterpretCast(const CXXReinterpretCastExpr *CastE, + const nonloc::PointerToMember &PTM) { + // Retrieve the class/struct whose member-pointer the expression will be + // declared as. It will be the one written as target type of the + // reinterpret_cast. + const MemberPointerType *StartPTMTy = + CastE->getTypeAsWritten().getTypePtr()->getAs(); + const CXXRecordDecl *Start = StartPTMTy->getMostRecentCXXRecordDecl(); + const Type *StartTy = Start->getTypeForDecl(); + + const NamedDecl *ND; + llvm::ImmutableList BaseSpecList; + nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData(); + if (PTMDT.isNull() || PTMDT.is()) { + if (PTMDT.is()) + ND = PTMDT.get(); + } else { + const PointerToMemberData *PTMD = PTMDT.get(); + ND = PTMD->getDeclaratorDecl(); + } + + // Retrieve the class/struct which actually contains the field/method being + // pointed to + const CXXRecordDecl *Target = retrieveCXXRecord(ND); + if (!Target) + return nullptr; + const Type *TargetTy = Target->getTypeForDecl(); + + // If TargetTy and StartTy are the same, we can return an empty list + if (TargetTy == StartTy) + return getPointerToMemberData(ND, CXXBaseListFactory.getEmptyList()); + + // If Target is higher up than Start in the class hierarchy + const auto UpPath = findPathToBase(Start, Target); + if (UpPath.second) { + for (const CXXBaseSpecifier *BaseSpec : UpPath.first) + BaseSpecList = CXXBaseListFactory.add(BaseSpec, BaseSpecList); + return getPointerToMemberData(ND, BaseSpecList); + } + + // Otherwise Target is lower in the class hierarchy as Start, in which case + // the sub-object is unreachable. Otherwise Target is unrelated to Start. In + // either case, it makes no sense to continue further analysis, so we return + // nullptr + return nullptr; +} + const llvm::APSInt* BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, const llvm::APSInt& V1, const llvm::APSInt& V2) { diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -522,8 +522,7 @@ continue; } case CK_DerivedToBaseMemberPointer: - case CK_BaseToDerivedMemberPointer: - case CK_ReinterpretMemberPointer: { + case CK_BaseToDerivedMemberPointer: { SVal V = state->getSVal(Ex, LCtx); if (auto PTMSV = V.getAs()) { SVal CastedPTMSV = @@ -537,6 +536,25 @@ state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred); continue; } + case CK_ReinterpretMemberPointer: { + SVal V = state->getSVal(Ex, LCtx); + if (auto PTMSV = V.getAs()) { + if (const auto *ReinterpretE = + llvm::dyn_cast(CastE)) { + if (const PointerToMemberData *Data = + getBasicVals().basesForReinterpretCast(ReinterpretE, + *PTMSV)) { + SVal CastedPTMSV = svalBuilder.makePointerToMember(Data); + state = state->BindExpr(CastE, LCtx, CastedPTMSV); + Bldr.generateNode(CastE, Pred, state); + continue; + } + } + } + // Explicitly proceed with default handler for this case cascade. + state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred); + continue; + } // Various C++ casts that are not handled yet. case CK_ToUnion: case CK_VectorSplat: { diff --git a/clang/test/Analysis/reinterpret-cast-pointer-to-member.cpp b/clang/test/Analysis/reinterpret-cast-pointer-to-member.cpp --- a/clang/test/Analysis/reinterpret-cast-pointer-to-member.cpp +++ b/clang/test/Analysis/reinterpret-cast-pointer-to-member.cpp @@ -1,11 +1,11 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s -// XFAIL: asserts void clang_analyzer_eval(bool); -// TODO: The following test will work properly once reinterpret_cast on pointer-to-member is handled properly namespace testReinterpretCasting { -struct Base { +struct BaseOfBase {}; + +struct Base : public BaseOfBase { int field; }; @@ -15,12 +15,51 @@ struct Some {}; -void f() { +void analyzableCasts() { int DoubleDerived::*ddf = &Base::field; int Base::*bf = reinterpret_cast(reinterpret_cast(reinterpret_cast(ddf))); - int Some::*sf = reinterpret_cast(ddf); Base base; base.field = 13; clang_analyzer_eval(base.*bf == 13); // expected-warning{{TRUE}} } + +void castOutsideHierarchy() { + int DoubleDerived::*ddf = &Base::field; + int Some::*sf = reinterpret_cast(ddf); + Some some; + some.*sf = 14; + clang_analyzer_eval(some.*sf == 14); // expected-warning{{UNKNOWN}} +} + +void castAbove() { + int DoubleDerived::*ddf = &Base::field; + int BaseOfBase::*bbf = reinterpret_cast(ddf); + BaseOfBase bb; + bb.*bbf = 23; + clang_analyzer_eval(bb.*bbf == 23); // expected-warning{{UNKNOWN}} +} + +namespace testMultipleInheritance { +struct A {}; +struct B : public A {}; +struct C { + int field; +}; +struct D : public C {}; +struct E : public B, public D {}; +struct F : public E {}; + +void testMultiple() { + int F::*f = &F::field; + int A::*a = reinterpret_cast(f); + int C::*c = reinterpret_cast(f); + A aobj; + C cobj; + aobj.*a = 13; + cobj.field = 29; + clang_analyzer_eval(aobj.*a == 13); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(cobj.*c == 29); // expected-warning{{TRUE}} +} +} // namespace testMultipleInheritance + } // namespace testReinterpretCasting