diff options
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngine.cpp | 7 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineC.cpp | 12 | ||||
-rw-r--r-- | test/Analysis/pointer-to-member.cpp | 44 |
3 files changed, 60 insertions, 3 deletions
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index d9b7ff57bf..26516938d9 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1428,7 +1428,12 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, } if (isa<FieldDecl>(D)) { // FIXME: Compute lvalue of field pointers-to-member. - Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, UnknownVal()), 0, + // Right now we just use a non-null void pointer, so that it gives proper + // results in boolean contexts. + SVal V = svalBuilder.conjureSymbolVal(Ex, LCtx, getContext().VoidPtrTy, + currBldrCtx->blockCount()); + state = state->assume(cast<DefinedOrUnknownSVal>(V), true); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), 0, ProgramPoint::PostLValueKind); return; } diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index f65a6ebc34..8608f993be 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -274,6 +274,9 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Bldr.generateNode(CastE, Pred, state); continue; } + case CK_MemberPointerToBoolean: + // FIXME: For now, member pointers are represented by void *. + // FALLTHROUGH case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: @@ -359,16 +362,21 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Bldr.generateNode(CastE, Pred, state); continue; } + case CK_NullToMemberPointer: { + // FIXME: For now, member pointers are represented by void *. + SVal V = svalBuilder.makeIntValWithPtrWidth(0, true); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } // Various C++ casts that are not handled yet. case CK_ToUnion: case CK_BaseToDerived: - case CK_NullToMemberPointer: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: case CK_ReinterpretMemberPointer: case CK_ConstructorConversion: case CK_VectorSplat: - case CK_MemberPointerToBoolean: case CK_LValueBitCast: { // Recover some path-sensitivty by conjuring a new value. QualType resultType = CastE->getType(); diff --git a/test/Analysis/pointer-to-member.cpp b/test/Analysis/pointer-to-member.cpp new file mode 100644 index 0000000000..cef5dc5866 --- /dev/null +++ b/test/Analysis/pointer-to-member.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=inlining -verify %s + +void clang_analyzer_eval(bool); + +struct A { + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef A * (A::*MemberPointer); + operator MemberPointer() const { return m_ptr ? &A::m_ptr : 0; } + + A *m_ptr; +}; + +void testConditionalUse() { + A obj; + + obj.m_ptr = &obj; + clang_analyzer_eval(obj.m_ptr); // expected-warning{{TRUE}} + clang_analyzer_eval(&A::m_ptr); // expected-warning{{TRUE}} + clang_analyzer_eval(obj); // expected-warning{{TRUE}} + + obj.m_ptr = 0; + clang_analyzer_eval(obj.m_ptr); // expected-warning{{FALSE}} + clang_analyzer_eval(A::MemberPointer(0)); // expected-warning{{FALSE}} + clang_analyzer_eval(obj); // expected-warning{{FALSE}} +} + +// --------------- +// 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}} + + member = 0; + + // FIXME: Should emit a null dereference. + return obj.*member; // no-warning +} |