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 @@ -79,11 +79,11 @@ }; class PointerToMemberData : public llvm::FoldingSetNode { - const DeclaratorDecl *D; + const NamedDecl *D; llvm::ImmutableList L; public: - PointerToMemberData(const DeclaratorDecl *D, + PointerToMemberData(const NamedDecl *D, llvm::ImmutableList L) : D(D), L(L) {} @@ -92,11 +92,11 @@ iterator begin() const { return L.begin(); } iterator end() const { return L.end(); } - static void Profile(llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D, + static void Profile(llvm::FoldingSetNodeID &ID, const NamedDecl *D, llvm::ImmutableList L); - void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, D, L); } - const DeclaratorDecl *getDeclaratorDecl() const {return D;} + void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, D, L); } + const NamedDecl *getDeclaratorDecl() const { return D; } llvm::ImmutableList getCXXBaseList() const { return L; @@ -236,9 +236,9 @@ const LazyCompoundValData *getLazyCompoundValData(const StoreRef &store, const TypedValueRegion *region); - const PointerToMemberData *getPointerToMemberData( - const DeclaratorDecl *DD, - llvm::ImmutableList L); + const PointerToMemberData * + getPointerToMemberData(const NamedDecl *ND, + llvm::ImmutableList L); llvm::ImmutableList getEmptySValList() { return SValListFactory.getEmptyList(); diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -233,7 +233,7 @@ const LocationContext *LCtx, unsigned count); - DefinedSVal getMemberPointer(const DeclaratorDecl *DD); + DefinedSVal getMemberPointer(const NamedDecl *ND); DefinedSVal getFunctionPointer(const FunctionDecl *func); diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -520,7 +520,7 @@ public: using PTMDataType = - llvm::PointerUnion; + llvm::PointerUnion; const PTMDataType getPTMData() const { return PTMDataType::getFromOpaqueValue(const_cast(Data)); @@ -528,7 +528,7 @@ bool isNullMemberPointer() const; - const DeclaratorDecl *getDecl() const; + const NamedDecl *getDecl() const; template const AdjustedDecl *getDeclAs() const { 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 @@ -42,7 +42,7 @@ } void PointerToMemberData::Profile( - llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D, + llvm::FoldingSetNodeID &ID, const NamedDecl *D, llvm::ImmutableList L) { ID.AddPointer(D); ID.AddPointer(L.getInternalPointer()); @@ -159,17 +159,17 @@ } const PointerToMemberData *BasicValueFactory::getPointerToMemberData( - const DeclaratorDecl *DD, llvm::ImmutableList L) { + const NamedDecl *ND, llvm::ImmutableList L) { llvm::FoldingSetNodeID ID; - PointerToMemberData::Profile(ID, DD, L); + PointerToMemberData::Profile(ID, ND, L); void *InsertPos; PointerToMemberData *D = PointerToMemberDataSet.FindNodeOrInsertPos(ID, InsertPos); if (!D) { - D = (PointerToMemberData*) BPAlloc.Allocate(); - new (D) PointerToMemberData(DD, L); + D = (PointerToMemberData *)BPAlloc.Allocate(); + new (D) PointerToMemberData(ND, L); PointerToMemberDataSet.InsertNode(D, InsertPos); } @@ -180,25 +180,24 @@ llvm::iterator_range PathRange, const nonloc::PointerToMember &PTM) { nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData(); - const DeclaratorDecl *DD = nullptr; + const NamedDecl *ND = nullptr; llvm::ImmutableList PathList; - if (PTMDT.isNull() || PTMDT.is()) { - if (PTMDT.is()) - DD = PTMDT.get(); + if (PTMDT.isNull() || PTMDT.is()) { + if (PTMDT.is()) + ND = PTMDT.get(); PathList = CXXBaseListFactory.getEmptyList(); } else { // const PointerToMemberData * - const PointerToMemberData *PTMD = - PTMDT.get(); - DD = PTMD->getDeclaratorDecl(); + const PointerToMemberData *PTMD = PTMDT.get(); + ND = PTMD->getDeclaratorDecl(); PathList = PTMD->getCXXBaseList(); } for (const auto &I : llvm::reverse(PathRange)) PathList = prependCXXBase(I, PathList); - return getPointerToMemberData(DD, PathList); + return getPointerToMemberData(ND, PathList); } const llvm::APSInt* diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -2530,16 +2530,8 @@ return; } if (isa(D) || isa(D)) { - // FIXME: Compute lvalue of field pointers-to-member. - // Right now we just use a non-null void pointer, so that it gives proper - // results in boolean contexts. - // FIXME: Maybe delegate this to the surrounding operator&. - // Note how this expression is lvalue, however pointer-to-member is NonLoc. - SVal V = svalBuilder.conjureSymbolVal(Ex, LCtx, getContext().VoidPtrTy, - currBldrCtx->blockCount()); - state = state->assume(V.castAs(), true); - Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, - ProgramPoint::PostLValueKind); + // Delegate all work related to pointer to members to the surrounding + // operator&. return; } if (isa(D)) { 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 @@ -991,10 +991,11 @@ if (const DeclRefExpr *DRE = dyn_cast(Ex)) { const ValueDecl *VD = DRE->getDecl(); - if (isa(VD) || isa(VD)) { + if (isa(VD) || isa(VD) || + isa(VD)) { ProgramStateRef State = (*I)->getState(); const LocationContext *LCtx = (*I)->getLocationContext(); - SVal SV = svalBuilder.getMemberPointer(cast(VD)); + SVal SV = svalBuilder.getMemberPointer(cast(VD)); Bldr.generateNode(U, *I, State->BindExpr(U, LCtx, SV)); break; } diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp --- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -236,10 +236,11 @@ return nonloc::SymbolVal(sym); } -DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) { - assert(!DD || isa(DD) || isa(DD)); +DefinedSVal SValBuilder::getMemberPointer(const NamedDecl *ND) { + assert(!ND || isa(ND) || isa(ND) || + isa(ND)); - if (const auto *MD = dyn_cast_or_null(DD)) { + if (const auto *MD = dyn_cast_or_null(ND)) { // Sema treats pointers to static member functions as have function pointer // type, so return a function pointer for the method. // We don't need to play a similar trick for static member fields @@ -249,7 +250,7 @@ return getFunctionPointer(MD); } - return nonloc::PointerToMember(DD); + return nonloc::PointerToMember(ND); } DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) { diff --git a/clang/lib/StaticAnalyzer/Core/SVals.cpp b/clang/lib/StaticAnalyzer/Core/SVals.cpp --- a/clang/lib/StaticAnalyzer/Core/SVals.cpp +++ b/clang/lib/StaticAnalyzer/Core/SVals.cpp @@ -157,18 +157,18 @@ return getPTMData().isNull(); } -const DeclaratorDecl *nonloc::PointerToMember::getDecl() const { +const NamedDecl *nonloc::PointerToMember::getDecl() const { const auto PTMD = this->getPTMData(); if (PTMD.isNull()) return nullptr; - const DeclaratorDecl *DD = nullptr; - if (PTMD.is()) - DD = PTMD.get(); + const NamedDecl *ND = nullptr; + if (PTMD.is()) + ND = PTMD.get(); else - DD = PTMD.get()->getDeclaratorDecl(); + ND = PTMD.get()->getDeclaratorDecl(); - return DD; + return ND; } //===----------------------------------------------------------------------===// @@ -185,14 +185,14 @@ nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const { const PTMDataType PTMD = getPTMData(); - if (PTMD.is()) + if (PTMD.is()) return {}; return PTMD.get()->begin(); } nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const { const PTMDataType PTMD = getPTMData(); - if (PTMD.is()) + if (PTMD.is()) return {}; return PTMD.get()->end(); } diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp --- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -1106,19 +1106,28 @@ } SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, - BinaryOperator::Opcode op, - Loc lhs, NonLoc rhs, QualType resultTy) { + 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()) { + + auto getFieldLValue = [&](const auto *FD) -> SVal { SVal Result = lhs; for (const auto &I : *PTMSV) Result = StateMgr.getStoreManager().evalDerivedToBase( - Result, I->getType(),I->isVirtual()); + Result, I->getType(), I->isVirtual()); + return state->getLValue(FD, Result); + }; + + if (const auto *FD = PTMSV->getDeclAs()) { + return getFieldLValue(FD); + } + if (const auto *FD = PTMSV->getDeclAs()) { + return getFieldLValue(FD); } } diff --git a/clang/test/Analysis/PR46264.cpp b/clang/test/Analysis/PR46264.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/PR46264.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s + +// rdar://problem/64202361 + +namespace a { +class b { +public: + typedef int b::*c; + operator c() { return &b::d; } + int d; +}; +} // namespace a + +using a::b; +class e { + void f(); + void g(); + b h; +}; + +void e::f() { + e *i; + if (h) // no crash + i->g(); // expected-warning{{Called C++ object pointer is uninitialized}} +} diff --git a/clang/test/Analysis/pointer-to-member.cpp b/clang/test/Analysis/pointer-to-member.cpp --- a/clang/test/Analysis/pointer-to-member.cpp +++ b/clang/test/Analysis/pointer-to-member.cpp @@ -233,39 +233,57 @@ namespace testAnonymousMember { struct A { + int a; struct { - int x; + int b; + int c; }; struct { struct { - int y; + int d; + int e; }; }; struct { union { - int z; + int f; }; }; }; void test() { - clang_analyzer_eval(&A::x); // expected-warning{{TRUE}} - clang_analyzer_eval(&A::y); // expected-warning{{TRUE}} - clang_analyzer_eval(&A::z); // expected-warning{{TRUE}} + clang_analyzer_eval(&A::a); // expected-warning{{TRUE}} + clang_analyzer_eval(&A::b); // expected-warning{{TRUE}} + clang_analyzer_eval(&A::c); // expected-warning{{TRUE}} + clang_analyzer_eval(&A::d); // expected-warning{{TRUE}} + clang_analyzer_eval(&A::e); // expected-warning{{TRUE}} + clang_analyzer_eval(&A::f); // expected-warning{{TRUE}} + + int A::*ap = &A::a, + A::*bp = &A::b, + A::*cp = &A::c, + A::*dp = &A::d, + A::*ep = &A::e, + A::*fp = &A::f; + + clang_analyzer_eval(ap); // expected-warning{{TRUE}} + clang_analyzer_eval(bp); // expected-warning{{TRUE}} + clang_analyzer_eval(cp); // expected-warning{{TRUE}} + clang_analyzer_eval(dp); // expected-warning{{TRUE}} + clang_analyzer_eval(ep); // expected-warning{{TRUE}} + clang_analyzer_eval(fp); // expected-warning{{TRUE}} - // FIXME: These should be true. - int A::*l = &A::x, A::*m = &A::y, A::*n = &A::z; - clang_analyzer_eval(l); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(m); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(n); // expected-warning{{UNKNOWN}} - - // FIXME: These should be true as well. A a; - a.x = 1; - clang_analyzer_eval(a.*l == 1); // expected-warning{{UNKNOWN}} - a.y = 2; - clang_analyzer_eval(a.*m == 2); // expected-warning{{UNKNOWN}} - a.z = 3; - clang_analyzer_eval(a.*n == 3); // expected-warning{{UNKNOWN}} + a.a = 1; + a.b = 2; + a.c = 3; + a.d = 4; + a.e = 5; + + clang_analyzer_eval(a.*ap == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(a.*bp == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(a.*cp == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(a.*dp == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(a.*ep == 5); // expected-warning{{TRUE}} } -} // end of testAnonymousMember namespace +} // namespace testAnonymousMember