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 @@ -22,8 +22,11 @@ #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" #include #include +#include +#include #include using namespace clang; @@ -239,13 +242,118 @@ 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); } +struct BFSNode { + const CXXBaseSpecifier *BaseSpec; + std::shared_ptr Parent; + + BFSNode(const CXXBaseSpecifier *BaseSpec, + std::shared_ptr Parent = nullptr) + : BaseSpec{BaseSpec}, Parent{Parent} {} +}; + +std::pair, bool> +findPathToBase(const Type *Start, const Type *End) { + std::deque> queue; + llvm::SmallVector Path; + const CXXRecordDecl *StartDecl = Start->getAsCXXRecordDecl(); + if (!StartDecl) + return {Path, false}; + + for (auto It = StartDecl->bases_begin(), E = StartDecl->bases_end(); It != E; + ++It) + queue.push_back(std::make_shared(It)); + + while (!queue.empty()) { + std::shared_ptr CurrNode = queue.front(); + queue.pop_front(); + const CXXBaseSpecifier *CurrBaseSpec = CurrNode->BaseSpec; + const Type *CurrType = CurrBaseSpec->getType().getTypePtr(); + if (CurrType == End) { + BFSNode *Ptr = CurrNode.get(); + while (Ptr) { + Path.push_back(Ptr->BaseSpec); + Ptr = Ptr->Parent.get(); + } + std::reverse(Path.begin(), Path.end()); + return {Path, true}; + } + const CXXRecordDecl *CurrRecordDecl = CurrType->getAsCXXRecordDecl(); + assert(CurrRecordDecl && + "Base of a CXX class/struct must be a CXX class/struct"); + for (auto It = CurrRecordDecl->bases_begin(), + E = CurrRecordDecl->bases_end(); + It != E; ++It) + queue.push_back(std::make_shared(It, CurrNode)); + } + + return {Path, false}; +} + +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; +} + +const PointerToMemberData * +BasicValueFactory::basesForReinterpretCast(const CXXReinterpretCastExpr *CastE, + const nonloc::PointerToMember &PTM) { + 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(); + } + + 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(StartTy, TargetTy); + 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,5 +1,4 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s -// XFAIL: asserts void clang_analyzer_eval(bool);