From 0156439a3d718ea0ef5922c38d189a60829c8a86 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Thu, 23 Aug 2012 23:01:43 +0000 Subject: [PATCH] [analyzer] For now, treat pointers-to-members as non-null void * symbols. Until we have full support for pointers-to-members, we can at least approximate some of their use by tracking null and non-null values. We thus treat &A::m_ptr as a non-null void * symbol, and MemberPointer(0) as a pointer-sized null constant. This enables support for what is sometimes called the "safe bool" idiom, demonstrated in the test case. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162495 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/StaticAnalyzer/Core/ExprEngine.cpp | 7 +++- lib/StaticAnalyzer/Core/ExprEngineC.cpp | 12 +++++-- test/Analysis/pointer-to-member.cpp | 44 +++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 test/Analysis/pointer-to-member.cpp 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(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(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 +} -- 2.40.0