Index: include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -59,6 +59,29 @@ void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, store, region); } }; +class PointerToMemberData: public llvm::FoldingSetNode { + const DeclaratorDecl *D; + llvm::ImmutableList L; + +public: + PointerToMemberData(const DeclaratorDecl *D, + llvm::ImmutableList L) + : D(D), L(L) {} + + typedef llvm::ImmutableList::iterator iterator; + iterator begin() const { return L.begin(); } + iterator end() const { return L.end(); } + + static void Profile(llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D, + llvm::ImmutableList L); + + void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, D, L); } + const DeclaratorDecl *getDeclaratorDecl() const {return D;} + llvm::ImmutableList getCXXBaseList() const { + return L; + } +}; + class BasicValueFactory { typedef llvm::FoldingSet > APSIntSetTy; @@ -71,8 +94,10 @@ void * PersistentSValPairs; llvm::ImmutableList::Factory SValListFactory; + llvm::ImmutableList::Factory CXXBaseListFactory; llvm::FoldingSet CompoundValDataSet; llvm::FoldingSet LazyCompoundValDataSet; + llvm::FoldingSet PointerToMemberDataSet; // This is private because external clients should use the factory // method that takes a QualType. @@ -81,7 +106,8 @@ public: BasicValueFactory(ASTContext &ctx, llvm::BumpPtrAllocator &Alloc) : Ctx(ctx), BPAlloc(Alloc), PersistentSVals(nullptr), - PersistentSValPairs(nullptr), SValListFactory(Alloc) {} + PersistentSValPairs(nullptr), SValListFactory(Alloc), + CXXBaseListFactory(Alloc) {} ~BasicValueFactory(); @@ -172,14 +198,32 @@ const LazyCompoundValData *getLazyCompoundValData(const StoreRef &store, const TypedValueRegion *region); + const PointerToMemberData *getPointerToMemberData( + const DeclaratorDecl *DD, + llvm::ImmutableList L); + llvm::ImmutableList getEmptySValList() { return SValListFactory.getEmptyList(); } - llvm::ImmutableList consVals(SVal X, llvm::ImmutableList L) { + llvm::ImmutableList prependSVal(SVal X, llvm::ImmutableList L) { return SValListFactory.add(X, L); } + llvm::ImmutableList getEmptyCXXBaseList() { + return CXXBaseListFactory.getEmptyList(); + } + + llvm::ImmutableList prependCXXBase( + const CXXBaseSpecifier *CBS, + llvm::ImmutableList L) { + return CXXBaseListFactory.add(CBS, L); + } + + const clang::ento::PointerToMemberData *accumCXXBase( + llvm::iterator_range PathRange, + const nonloc::PointerToMember &PTM); + const llvm::APSInt* evalAPSInt(BinaryOperator::Opcode Op, const llvm::APSInt& V1, const llvm::APSInt& V2); Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -479,6 +479,22 @@ return X.isValid() ? svalBuilder.evalComplement(X.castAs()) : X; } + ProgramStateRef handleLValueBitCast(ProgramStateRef state, const Expr *Ex, + const LocationContext *LCtx, QualType T, + QualType ExTy, const CastExpr *CastE, + StmtNodeBuilder &Bldr, + ExplodedNode *Pred); + + ProgramStateRef handleLVectorSplat(ProgramStateRef state, + const LocationContext *LCtx, + const CastExpr *CastE, + StmtNodeBuilder &Bldr, + ExplodedNode *Pred); + + void handleUOExtension(ExplodedNodeSet::iterator I, + const UnaryOperator* U, + StmtNodeBuilder &Bldr); + public: SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, Index: include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -204,6 +204,8 @@ const LocationContext *LCtx, unsigned count); + DefinedSVal getMemberPointer(const DeclaratorDecl *DD); + DefinedSVal getFunctionPointer(const FunctionDecl *func); DefinedSVal getBlockPointer(const BlockDecl *block, CanQualType locTy, @@ -226,6 +228,14 @@ BasicVals.getLazyCompoundValData(store, region)); } + NonLoc makePointerToMember(const DeclaratorDecl *DD) { + return nonloc::PointerToMember(DD); + } + + NonLoc makePointerToMember(const PointerToMemberData *PTMD) { + return nonloc::PointerToMember(PTMD); + } + NonLoc makeZeroArrayIndex() { return nonloc::ConcreteInt(BasicVals.getValue(0, ArrayIndexTy)); } Index: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -32,6 +32,7 @@ class CompoundValData; class LazyCompoundValData; +class PointerToMemberData; class ProgramState; class BasicValueFactory; class MemRegion; @@ -459,6 +460,51 @@ } }; +/// \brief Value representing pointer-to-member. +/// +/// This value is qualified as NonLoc because neither loading nor storing +/// operations are aplied to it. Instead, the analyzer uses the L-value coming +/// from pointer-to-member applied to an object. +/// This SVal is represented by a DeclaratorDecl which can be a member function +/// pointer or a member data pointer and a list of CXXBaseSpecifiers. This list +/// is required to accumulate the pointer-to-member cast history to figure out +/// the correct subobject field. +class PointerToMember : public NonLoc { + friend class ento::SValBuilder; + +public: + typedef llvm::PointerUnion PTMDataType; + const PTMDataType getPTMData() const { + return PTMDataType::getFromOpaqueValue(const_cast(Data)); + } + bool isNullMemberPointer() const { + return getPTMData().isNull(); + } + const DeclaratorDecl *getDecl() const; + template + const AdjustedDecl* getDeclAs() const { + return dyn_cast_or_null(getDecl()); + } + typedef llvm::ImmutableList::iterator iterator; + iterator begin() const; + iterator end() const; + +private: + explicit PointerToMember(const PTMDataType D) + : NonLoc(PointerToMemberKind, D.getOpaqueValue()) {} + friend class SVal; + PointerToMember() {} + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == PointerToMemberKind; + } + + static bool isKind(const NonLoc& V) { + return V.getSubKind() == PointerToMemberKind; + } +}; + } // end namespace ento::nonloc //==------------------------------------------------------------------------==// Index: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def +++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def @@ -66,6 +66,7 @@ NONLOC_SVAL(LazyCompoundVal, NonLoc) NONLOC_SVAL(LocAsInteger, NonLoc) NONLOC_SVAL(SymbolVal, NonLoc) + NONLOC_SVAL(PointerToMember, NonLoc) #undef NONLOC_SVAL #undef LOC_SVAL Index: lib/StaticAnalyzer/Core/BasicValueFactory.cpp =================================================================== --- lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -33,6 +33,13 @@ ID.AddPointer(region); } +void PointerToMemberData::Profile( + llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D, + llvm::ImmutableList L) { + ID.AddPointer(D); + ID.AddPointer(L.getInternalPointer()); +} + typedef std::pair SValData; typedef std::pair SValPair; @@ -142,6 +149,49 @@ return D; } +const PointerToMemberData *BasicValueFactory::getPointerToMemberData( + const DeclaratorDecl *DD, llvm::ImmutableList L) { + llvm::FoldingSetNodeID ID; + PointerToMemberData::Profile(ID, DD, L); + void *InsertPos; + + PointerToMemberData *D = + PointerToMemberDataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!D) { + D = (PointerToMemberData*) BPAlloc.Allocate(); + new (D) PointerToMemberData(DD, L); + PointerToMemberDataSet.InsertNode(D, InsertPos); + } + + return D; +} + +const clang::ento::PointerToMemberData *BasicValueFactory::accumCXXBase( + llvm::iterator_range PathRange, + const nonloc::PointerToMember &PTM) { + nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData(); + const DeclaratorDecl *DD = nullptr; + llvm::ImmutableList PathList; + + if (PTMDT.isNull() || PTMDT.is()) { + if (PTMDT.is()) + DD = PTMDT.get(); + + PathList = CXXBaseListFactory.getEmptyList(); + } else { // const PointerToMemberData * + const PointerToMemberData *PTMD = + PTMDT.get(); + DD = PTMD->getDeclaratorDecl(); + + PathList = PTMD->getCXXBaseList(); + } + + for (const auto &I : llvm::reverse(PathRange)) + PathList = prependCXXBase(I, PathList); + return getPointerToMemberData(DD, PathList); +} + const llvm::APSInt* BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, const llvm::APSInt& V1, const llvm::APSInt& V2) { Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ExprCXX.h" +#include "clang/AST/DeclCXX.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" @@ -246,6 +247,38 @@ getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this); } +ProgramStateRef ExprEngine::handleLValueBitCast( + ProgramStateRef state, const Expr* Ex, const LocationContext* LCtx, + QualType T, QualType ExTy, const CastExpr* CastE, StmtNodeBuilder& Bldr, + ExplodedNode* Pred) { + // Delegate to SValBuilder to process. + SVal V = state->getSVal(Ex, LCtx); + V = svalBuilder.evalCast(V, T, ExTy); + // Negate the result if we're treating the boolean as a signed i1 + if (CastE->getCastKind() == CK_BooleanToSignedIntegral) + V = evalMinus(V); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + + return state; +} + +ProgramStateRef ExprEngine::handleLVectorSplat( + ProgramStateRef state, const LocationContext* LCtx, const CastExpr* CastE, + StmtNodeBuilder &Bldr, ExplodedNode* Pred) { + // Recover some path sensitivity by conjuring a new value. + QualType resultType = CastE->getType(); + if (CastE->isGLValue()) + resultType = getContext().getPointerType(resultType); + SVal result = svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, + resultType, + currBldrCtx->blockCount()); + state = state->BindExpr(CastE, LCtx, result); + Bldr.generateNode(CastE, Pred, state); + + return state; +} + void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { @@ -310,8 +343,21 @@ continue; } case CK_MemberPointerToBoolean: - // FIXME: For now, member pointers are represented by void *. - // FALLTHROUGH + case CK_PointerToBoolean: { + SVal V = state->getSVal(Ex, LCtx); + auto PTMSV = V.getAs(); + if (PTMSV) + V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy); + if (V.isUndef() || PTMSV) { + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Explicitly proceed with default handler for this case cascade. + state = + handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); + continue; + } case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: @@ -319,8 +365,18 @@ case CK_BooleanToSignedIntegral: case CK_NullToPointer: case CK_IntegralToPointer: - case CK_PointerToIntegral: - case CK_PointerToBoolean: + case CK_PointerToIntegral: { + SVal V = state->getSVal(Ex, LCtx); + if (V.getAs()) { + state = state->BindExpr(CastE, LCtx, UnknownVal()); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Explicitly proceed with default handler for this case cascade. + state = + handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); + continue; + } case CK_IntegralToBoolean: case CK_IntegralToFloating: case CK_FloatingToIntegral: @@ -343,14 +399,8 @@ case CK_ZeroToOCLEvent: case CK_IntToOCLSampler: case CK_LValueBitCast: { - // Delegate to SValBuilder to process. - SVal V = state->getSVal(Ex, LCtx); - V = svalBuilder.evalCast(V, T, ExTy); - // Negate the result if we're treating the boolean as a signed i1 - if (CastE->getCastKind() == CK_BooleanToSignedIntegral) - V = evalMinus(V); - state = state->BindExpr(CastE, LCtx, V); - Bldr.generateNode(CastE, Pred, state); + state = + handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); continue; } case CK_IntegralCast: { @@ -435,27 +485,32 @@ continue; } case CK_NullToMemberPointer: { - // FIXME: For now, member pointers are represented by void *. - SVal V = svalBuilder.makeNull(); + SVal V = svalBuilder.getMemberPointer(nullptr); state = state->BindExpr(CastE, LCtx, V); Bldr.generateNode(CastE, Pred, state); continue; } + case CK_DerivedToBaseMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_ReinterpretMemberPointer: { + SVal V = state->getSVal(Ex, LCtx); + if (auto PTMSV = V.getAs()) { + SVal CastedPTMSV = svalBuilder.makePointerToMember( + getBasicVals().accumCXXBase( + llvm::make_range( + CastE->path_begin(), CastE->path_end()), *PTMSV)); + 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_BaseToDerivedMemberPointer: - case CK_DerivedToBaseMemberPointer: - case CK_ReinterpretMemberPointer: case CK_VectorSplat: { - // Recover some path-sensitivty by conjuring a new value. - QualType resultType = CastE->getType(); - if (CastE->isGLValue()) - resultType = getContext().getPointerType(resultType); - SVal result = svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, - resultType, - currBldrCtx->blockCount()); - state = state->BindExpr(CastE, LCtx, result); - Bldr.generateNode(CastE, Pred, state); + state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred); continue; } } @@ -658,7 +713,7 @@ for (InitListExpr::const_reverse_iterator it = IE->rbegin(), ei = IE->rend(); it != ei; ++it) { SVal V = state->getSVal(cast(*it), LCtx); - vals = getBasicVals().consVals(V, vals); + vals = getBasicVals().prependSVal(V, vals); } B.generateNode(IE, Pred, @@ -803,8 +858,24 @@ getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); } -void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, - ExplodedNode *Pred, +void ExprEngine::handleUOExtension(ExplodedNodeSet::iterator I, + const UnaryOperator *U, + StmtNodeBuilder &Bldr) { + // FIXME: We can probably just have some magic in Environment::getSVal() + // that propagates values, instead of creating a new node here. + // + // Unary "+" is a no-op, similar to a parentheses. We still have places + // where it may be a block-level expression, so we need to + // generate an extra node that just propagates the value of the + // subexpression. + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + ProgramStateRef state = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, + state->getSVal(Ex, LCtx))); +} + +void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // FIXME: Prechecks eventually go in ::Visit(). ExplodedNodeSet CheckedSet; @@ -856,24 +927,30 @@ break; } + case UO_AddrOf: { + // Process pointer-to-member address operation. + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + if (const DeclRefExpr *DRE = dyn_cast(Ex)) { + const ValueDecl *VD = DRE->getDecl(); + + if (isa(VD) || isa(VD)) { + ProgramStateRef State = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + SVal SV = svalBuilder.getMemberPointer(cast(VD)); + Bldr.generateNode(U, *I, State->BindExpr(U, LCtx, SV)); + break; + } + } + // Explicitly proceed with default handler for this case cascade. + handleUOExtension(I, U, Bldr); + break; + } case UO_Plus: assert(!U->isGLValue()); // FALL-THROUGH. case UO_Deref: - case UO_AddrOf: case UO_Extension: { - // FIXME: We can probably just have some magic in Environment::getSVal() - // that propagates values, instead of creating a new node here. - // - // Unary "+" is a no-op, similar to a parentheses. We still have places - // where it may be a block-level expression, so we need to - // generate an extra node that just propagates the value of the - // subexpression. - const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ProgramStateRef state = (*I)->getState(); - const LocationContext *LCtx = (*I)->getLocationContext(); - Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, - state->getSVal(Ex, LCtx))); + handleUOExtension(I, U, Bldr); break; } Index: lib/StaticAnalyzer/Core/SValBuilder.cpp =================================================================== --- lib/StaticAnalyzer/Core/SValBuilder.cpp +++ lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -217,6 +217,10 @@ return nonloc::SymbolVal(sym); } +DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl* DD) { + return nonloc::PointerToMember(DD); +} + DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) { return loc::MemRegionVal(MemMgr.getFunctionCodeRegion(func)); } Index: lib/StaticAnalyzer/Core/SVals.cpp =================================================================== --- lib/StaticAnalyzer/Core/SVals.cpp +++ lib/StaticAnalyzer/Core/SVals.cpp @@ -16,6 +16,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/Basic/IdentifierTable.h" #include "llvm/Support/raw_ostream.h" +#include "clang/AST/DeclCXX.h" using namespace clang; using namespace ento; using llvm::APSInt; @@ -56,6 +57,10 @@ return FD; } + if (auto X = getAs()) { + if (const CXXMethodDecl *MD = dyn_cast_or_null(X->getDecl())) + return MD; + } return nullptr; } @@ -155,6 +160,20 @@ return static_cast(Data)->getRegion(); } +const DeclaratorDecl *nonloc::PointerToMember::getDecl() const { + const auto PTMD = this->getPTMData(); + if (PTMD.isNull()) + return nullptr; + + const DeclaratorDecl *DD = nullptr; + if (PTMD.is()) + DD = PTMD.get(); + else + DD = PTMD.get()->getDeclaratorDecl(); + + return DD; +} + //===----------------------------------------------------------------------===// // Other Iterators. //===----------------------------------------------------------------------===// @@ -167,6 +186,20 @@ return getValue()->end(); } +nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const { + const PTMDataType PTMD = getPTMData(); + if (PTMD.is()) + return nonloc::PointerToMember::iterator(); + return PTMD.get()->begin(); +} + +nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const { + const PTMDataType PTMD = getPTMData(); + if (PTMD.is()) + return nonloc::PointerToMember::iterator(); + return PTMD.get()->end(); +} + //===----------------------------------------------------------------------===// // Useful predicates. //===----------------------------------------------------------------------===// @@ -299,6 +332,26 @@ << '}'; break; } + case nonloc::PointerToMemberKind: { + os << "pointerToMember{"; + const nonloc::PointerToMember &CastRes = + castAs(); + if (CastRes.getDecl()) + os << "|" << CastRes.getDecl()->getQualifiedNameAsString() << "|"; + bool first = true; + for (const auto &I : CastRes) { + if (first) { + os << ' '; first = false; + } + else + os << ", "; + + os << (*I).getType().getAsString(); + } + + os << '}'; + break; + } default: assert (false && "Pretty-printed not implemented for this NonLoc."); break; Index: lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -182,6 +182,12 @@ return isFeasible ? State : nullptr; } + case nonloc::PointerToMemberKind: { + bool IsNull = !Cond.castAs().isNullMemberPointer(); + bool IsFeasible = IsNull ? Assumption : !Assumption; + return IsFeasible ? State : nullptr; + } + case nonloc::LocAsIntegerKind: return assume(State, Cond.castAs().getLoc(), Assumption); Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp =================================================================== --- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -69,6 +69,9 @@ bool isLocType = Loc::isLocType(castTy); + if (val.getAs()) + return val; + if (Optional LI = val.getAs()) { if (isLocType) return LI->getLoc(); @@ -335,6 +338,21 @@ switch (lhs.getSubKind()) { default: return makeSymExprValNN(state, op, lhs, rhs, resultTy); + case nonloc::PointerToMemberKind: { + assert(rhs.getSubKind() == nonloc::PointerToMemberKind && + "Both SVals should have pointer-to-member-type"); + auto LPTM = lhs.castAs(), + RPTM = rhs.castAs(); + auto LPTMD = LPTM.getPTMData(), RPTMD = RPTM.getPTMData(); + switch (op) { + case BO_EQ: + return makeTruthVal(LPTMD == RPTMD, resultTy); + case BO_NE: + return makeTruthVal(LPTMD != RPTMD, resultTy); + default: + return UnknownVal(); + } + } case nonloc::LocAsIntegerKind: { Loc lhsL = lhs.castAs().getLoc(); switch (rhs.getSubKind()) { @@ -863,6 +881,23 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy) { + if (op >= BO_PtrMemD && op <= BO_PtrMemI) { + if (auto PTMSV = rhs.getAs()) { + if (PTMSV->isNullMemberPointer()) + return UndefinedVal(); + if (const FieldDecl *FD = PTMSV->getDeclAs()) { + SVal Result = lhs; + + for (const auto &I : *PTMSV) + Result = StateMgr.getStoreManager().evalDerivedToBase( + Result, I->getType(),I->isVirtual()); + return state->getLValue(FD, Result); + } + } + + return rhs; + } + assert(!BinaryOperator::isComparisonOp(op) && "arguments to comparison ops must be of the same type"); Index: test/Analysis/pointer-to-member.cpp =================================================================== --- test/Analysis/pointer-to-member.cpp +++ test/Analysis/pointer-to-member.cpp @@ -35,8 +35,7 @@ clang_analyzer_eval(&A::getPtr == &A::getPtr); // expected-warning{{TRUE}} clang_analyzer_eval(&A::getPtr == 0); // expected-warning{{FALSE}} - // FIXME: Should be TRUE. - clang_analyzer_eval(&A::m_ptr == &A::m_ptr); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(&A::m_ptr == &A::m_ptr); // expected-warning{{TRUE}} } namespace PR15742 { @@ -62,21 +61,156 @@ } } -// --------------- -// FALSE NEGATIVES -// --------------- - bool testDereferencing() { A obj; obj.m_ptr = 0; A::MemberPointer member = &A::m_ptr; - // FIXME: Should be TRUE. - clang_analyzer_eval(obj.*member == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(obj.*member == 0); // expected-warning{{TRUE}} member = 0; - // FIXME: Should emit a null dereference. - return obj.*member; // no-warning + return obj.*member; // expected-warning{{The result of the '.*' expression is undefined}} +} + +namespace testPointerToMemberFunction { + struct A { + virtual int foo() { return 1; } + int bar() { return 2; } + }; + + struct B : public A { + virtual int foo() { return 3; } + }; + + typedef int (A::*AFnPointer)(); + typedef int (B::*BFnPointer)(); + + void testPointerToMemberCasts() { + AFnPointer AFP = &A::bar; + BFnPointer StaticCastedBase2Derived = static_cast(&A::bar), + CCastedBase2Derived = (BFnPointer) (&A::bar); + A a; + B b; + + clang_analyzer_eval((a.*AFP)() == 2); // expected-warning{{TRUE}} + clang_analyzer_eval((b.*StaticCastedBase2Derived)() == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(((b.*CCastedBase2Derived)() == 2)); // expected-warning{{TRUE}} + } + + void testPointerToMemberVirtualCall() { + A a; + B b; + A *APtr = &a; + AFnPointer AFP = &A::foo; + + clang_analyzer_eval((APtr->*AFP)() == 1); // expected-warning{{TRUE}} + + APtr = &b; + + clang_analyzer_eval((APtr->*AFP)() == 3); // expected-warning{{TRUE}} + } +} // end of testPointerToMemberFunction namespace + +namespace testPointerToMemberData { + struct A { + int i; + }; + + void testPointerToMemberData() { + int A::*AMdPointer = &A::i; + A a; + + a.i = 42; + a.*AMdPointer += 1; + + clang_analyzer_eval(a.i == 43); // expected-warning{{TRUE}} + } +} // end of testPointerToMemberData namespace + +namespace testPointerToMemberMiscCasts { +struct B { + int f; +}; + +struct D : public B { + int g; +}; + +void foo() { + D d; + d.f = 7; + + int B::* pfb = &B::f; + int D::* pfd = pfb; + int v = d.*pfd; + + clang_analyzer_eval(v == 7); // expected-warning{{TRUE}} +} +} // end of testPointerToMemberMiscCasts namespace + +namespace testPointerToMemberMiscCasts2 { +struct B { + int f; +}; +struct L : public B { }; +struct R : public B { }; +struct D : public L, R { }; + +void foo() { + D d; + + int B::* pb = &B::f; + int L::* pl = pb; + int R::* pr = pb; + + int D::* pdl = pl; + int D::* pdr = pr; + + clang_analyzer_eval(pdl == pdr); // expected-warning{{FALSE}} + clang_analyzer_eval(pb == pl); // expected-warning{{TRUE}} +} +} // end of testPointerToMemberMiscCasts2 namespace + +namespace testPointerToMemberDiamond { +struct B { + int f; +}; +struct L1 : public B { }; +struct R1 : public B { }; +struct M : public L1, R1 { }; +struct L2 : public M { }; +struct R2 : public M { }; +struct D2 : public L2, R2 { }; + +void diamond() { + M m; + + static_cast(&m)->f = 7; + static_cast(&m)->f = 16; + + int L1::* pl1 = &B::f; + int M::* pm_via_l1 = pl1; + + int R1::* pr1 = &B::f; + int M::* pm_via_r1 = pr1; + + clang_analyzer_eval(m.*(pm_via_l1) == 7); // expected-warning {{TRUE}} + clang_analyzer_eval(m.*(pm_via_r1) == 16); // expected-warning {{TRUE}} +} + +void double_diamond() { + D2 d2; + + static_cast(static_cast(&d2))->f = 1; + static_cast(static_cast(&d2))->f = 2; + static_cast(static_cast(&d2))->f = 3; + static_cast(static_cast(&d2))->f = 4; + + clang_analyzer_eval(d2.*(static_cast(static_cast(static_cast(&B::f)))) == 1); // expected-warning {{TRUE}} + clang_analyzer_eval(d2.*(static_cast(static_cast(static_cast(&B::f)))) == 2); // expected-warning {{TRUE}} + clang_analyzer_eval(d2.*(static_cast(static_cast(static_cast(&B::f)))) == 3); // expected-warning {{TRUE}} + clang_analyzer_eval(d2.*(static_cast(static_cast(static_cast(&B::f)))) == 4); // expected-warning {{TRUE}} } +} // end of testPointerToMemberDiamond namespace